NCBI C Toolkit Cross Reference

C/vibrant/shim3d.c


  1 /*   shim3d.c
  2 * ===========================================================================
  3 *
  4 *                            PUBLIC DOMAIN NOTICE
  5 *            National Center for Biotechnology Information (NCBI)
  6 *
  7 *  This software/database is a "United States Government Work" under the
  8 *  terms of the United States Copyright Act.  It was written as part of
  9 *  the author's official duties as a United States Government employee and
 10 *  thus cannot be copyrighted.  This software/database is freely available
 11 *  to the public for use. The National Library of Medicine and the U.S.
 12 *  Government do not place any restriction on its use or reproduction.
 13 *  We would, however, appreciate having the NCBI and the author cited in
 14 *  any work or product based on this material
 15 *
 16 *  Although all reasonable efforts have been taken to ensure the accuracy
 17 *  and reliability of the software and data, the NLM and the U.S.
 18 *  Government do not and cannot warrant the performance or results that
 19 *  may be obtained by using this software or data. The NLM and the U.S.
 20 *  Government disclaim all warranties, express or implied, including
 21 *  warranties of performance, merchantability or fitness for any particular
 22 *  purpose.
 23 *
 24 * ===========================================================================
 25 *
 26 * File Name:  shim3d.c
 27 *
 28 * Author:  Lewis Geer
 29 *
 30 * Version Creation Date:   1/26/99
 31 *
 32 * $Revision: 6.79 $
 33 *
 34 * File Description: Shim functions to replace Viewer3D with OpenGL
 35 *
 36 * Modifications:
 37 * --------------------------------------------------------------------------
 38 * $Log: shim3d.c,v $
 39 * Revision 6.79  2002/03/28 13:35:48  kans
 40 * only include MoreCarbonAccessors.h if not OS_UNIX_DARWIN
 41 *
 42 * Revision 6.78  2001/05/25 19:46:58  vakatov
 43 * Nested comment typo fixed
 44 *
 45 * Revision 6.77  2001/04/23 16:05:45  juran
 46 * Include MoreCarbonAccessors.h, which now has GetPortText{Font,Face,Size}().
 47 *
 48 * Revision 6.76  2001/04/21 02:36:10  juran
 49 * Very simple Carbon fixes.
 50 *
 51 * Revision 6.75  2001/04/18 16:33:54  kans
 52 * moved define to first column
 53 *
 54 * Revision 6.74  2000/07/28 21:05:54  lewisg
 55 * more c++ fixes
 56 *
 57 * Revision 6.73  2000/07/27 16:34:46  lewisg
 58 * more c++ fixes
 59 *
 60 * Revision 6.72  2000/07/27 13:37:31  lewisg
 61 * more c++ fixes
 62 *
 63 * Revision 6.71  2000/07/25 12:38:26  thiessen
 64 * change C++-style comments to C
 65 *
 66 * Revision 6.70  2000/07/24 22:31:21  thiessen
 67 * fix header conflict
 68 *
 69 * Revision 6.69  2000/07/22 20:13:42  thiessen
 70 * fix header conflict
 71 *
 72 * Revision 6.68  2000/07/21 18:55:58  thiessen
 73 * allow dynamic slave->master transformation
 74 *
 75 * Revision 6.67  2000/05/16 17:38:44  thiessen
 76 * do glGenLists after context init on X11 - for Mesa 3.2
 77 *
 78 * Revision 6.66  2000/04/20 18:53:57  thiessen
 79 * misc tweaks/fixes
 80 *
 81 * Revision 6.65  2000/04/20 17:47:18  thiessen
 82 * tweak OpenGL drawing region position
 83 *
 84 * Revision 6.64  2000/04/19 17:56:46  thiessen
 85 * added background color in OpenGL
 86 *
 87 * Revision 6.63  2000/04/17 15:54:27  thiessen
 88 * add cylinder arrows; misc graphics tweaks
 89 *
 90 * Revision 6.62  2000/04/03 18:23:46  thiessen
 91 * add arrowheads to strand bricks
 92 *
 93 * Revision 6.61  2000/03/27 14:47:31  thiessen
 94 * widen logo slightly
 95 *
 96 * Revision 6.60  2000/03/24 20:34:59  lewisg
 97 * add blast from file, bug fixes, get rid of redundant code, etc.
 98 *
 99 * Revision 6.59  2000/03/24 19:59:20  thiessen
100 * draw new logo in OpenGL
101 *
102 * Revision 6.58  2000/03/22 23:42:22  lewisg
103 * timing loop for animation
104 *
105 * Revision 6.57  2000/03/15 16:59:53  thiessen
106 * fix highlighting, other minor bugs
107 *
108 * Revision 6.56  2000/03/09 17:55:18  thiessen
109 * changes to palette handling for 8-bit OpenGL
110 *
111 * Revision 6.55  2000/03/08 21:46:13  lewisg
112 * cn3d saves viewport, misc bugs
113 *
114 * Revision 6.54  2000/03/06 18:35:22  thiessen
115 * fixes for 8-bit color
116 *
117 * Revision 6.53  2000/02/28 19:53:08  kans
118 * if macintosh, include <gl.h> and <glu.h>, not equivalent <GL/gl.h> and <GL/glu.h>
119 *
120 * Revision 6.52  2000/02/26 13:30:41  thiessen
121 * capped cylinders and worms for visible ends
122 *
123 * Revision 6.51  2000/02/26 00:01:41  thiessen
124 * OpenGL improvements, progress on cleanup of Show/Hide
125 *
126 * Revision 6.50  2000/02/16 14:01:40  thiessen
127 * warning on OGL color black or alpha 0 (if _DEBUG)
128 *
129 * Revision 6.49  2000/02/10 17:48:10  thiessen
130 * added OGL zoom out
131 *
132 * Revision 6.48  2000/01/25 22:58:13  thiessen
133 * added animation of conf-ensembles
134 *
135 * Revision 6.47  2000/01/19 15:22:33  thiessen
136 * working off-screen GL rendering and PNG output on Mac
137 *
138 * Revision 6.46  2000/01/18 14:57:41  lewisg
139 * move OGL_qobj initialization to OGL_CreateViewer
140 *
141 * Revision 6.45  2000/01/14 21:40:39  lewisg
142 * add translucent spheres, ion labels, new cpk, fix misc bugs
143 *
144 * Revision 6.44  2000/01/12 15:13:00  thiessen
145 * fixed minor OpenGL memory leak
146 *
147 * Revision 6.43  2000/01/12 14:58:14  thiessen
148 * added progress monitor for PNG save
149 *
150 * Revision 6.42  2000/01/11 02:55:15  thiessen
151 * working off-screen OpenGL rendering on Win32
152 *
153 * Revision 6.41  2000/01/07 19:37:06  thiessen
154 * fix minor OpenGL problems
155 *
156 * Revision 6.40  2000/01/07 16:28:36  thiessen
157 * fixed broken header conflicts
158 *
159 * Revision 6.39  2000/01/07 00:22:45  thiessen
160 * fixes for LessTif and OpenGL X visual selection
161 *
162 * Revision 6.38  2000/01/06 17:41:53  kans
163 * Mac complained about True and False, changed to TRUE and FALSE
164 *
165 * Revision 6.37  2000/01/06 17:23:36  thiessen
166 * various OpenGL improvements
167 *
168 * Revision 6.36  2000/01/06 00:04:41  lewisg
169 * selection bug fixes, update message outbound, animation APIs moved to vibrant
170 *
171 * Revision 6.35  2000/01/03 14:41:10  thiessen
172 * fixed selection pointer inaccuracy
173 *
174 * Revision 6.34  1999/12/17 20:25:01  thiessen
175 * put in preliminary PNG output (for Cn3D)
176 *
177 * Revision 6.33  1999/12/15 20:02:49  thiessen
178 * added missing prototype
179 *
180 * Revision 6.32  1999/12/15 19:18:48  thiessen
181 * bug fix for previous revision
182 *
183 * Revision 6.30  1999/12/08 22:57:59  thiessen
184 * added quality settings for OpenGL rendering
185 *
186 * Revision 6.29  1999/12/07 19:18:58  thiessen
187 * fixed font color problem in OpenGL on SGI
188 *
189 * Revision 6.28  1999/12/07 15:46:17  thiessen
190 * fonts working in OpenGL on Mac
191 *
192 * Revision 6.27  1999/12/06 14:43:59  thiessen
193 * made OpenGL font selection work in X/Motif
194 *
195 * Revision 6.26  1999/12/03 15:55:01  thiessen
196 * added font styles and prettified OpenGL label panel
197 *
198 * Revision 6.25  1999/12/02 23:56:52  thiessen
199 * added font selection for OpenGL/Win32 labels
200 *
201 * Revision 6.24  1999/11/23 21:18:04  thiessen
202 * fixed Mac prototype problem
203 *
204 * Revision 6.23  1999/11/23 19:24:16  thiessen
205 * better solution to OpenGL render rect setting on Mac
206 *
207 * Revision 6.22  1999/11/23 18:41:34  thiessen
208 * fixed Mac OpenGL render rect bug
209 *
210 * Revision 6.21  1999/11/19 18:07:23  thiessen
211 * added label capability for OpenGL version on Mac and Motif
212 *
213 * Revision 6.20  1999/11/16 14:30:28  thiessen
214 * avoid white-blanking of OpenGL window on redraw
215 *
216 * Revision 6.19  1999/11/14 19:26:29  thiessen
217 * fixed broken select mode in OpenGL
218 *
219 * Revision 6.18  1999/11/10 17:17:27  thiessen
220 * fixed diffuse/ambient coloring in 8-bit opengl
221 *
222 * Revision 6.17  1999/11/10 15:25:38  thiessen
223 * partial fix for 8-bit OpenGL coloring
224 *
225 * Revision 6.16  1999/11/09 14:36:59  lewisg
226 * NT faults on write of const array
227 *
228 * Revision 6.15  1999/11/08 20:43:05  thiessen
229 * added much more thorough (but optional) GL error checking; see #define DEBUG_GL
230 *
231 * Revision 6.14  1999/11/08 19:46:30  thiessen
232 * fixed OpenGL transformation bug; also added check for GL error flag
233 *
234 * Revision 6.13  1999/11/08 16:43:20  thiessen
235 * major rearrangement of OpenGL color/material/lighting; also added 3-d (thick) brick
236 *
237 * Revision 6.12  1999/11/03 17:00:39  thiessen
238 * added capped cylinders for 'helix' object
239 *
240 * Revision 6.11  1999/10/31 22:39:34  thiessen
241 * added wifreframe worm capability to viewer3d
242 *
243 * Revision 6.10  1999/10/15 17:37:43  thiessen
244 * put in splined 'worm' model for virtual BB
245 *
246 * Revision 6.9  1999/10/04 18:05:34  thiessen
247 * fix minor glX problem with Motif
248 *
249 * Revision 6.8  1999/10/04 14:27:16  thiessen
250 * hacked partial compatibility with Mesa OpenGL implementation - does *not* work when rendering inside vibrant window in Motif
251 *
252 * Revision 6.7  1999/09/27 18:28:29  thiessen
253 * Made 24-bit and doublebuffered OpenGL modes work on Mac; also should select
254 * mode like X
255 *
256 * Revision 6.6  1999/09/27 16:29:29  thiessen
257 * added OpenGL buffer swap for X
258 *
259 * Revision 6.5  1999/09/22 14:22:49  lewisg
260 * fixed forward declaration of TOGL_Layers to compile on SGI
261 *
262 * Revision 6.4  1999/09/21 13:45:31  thiessen
263 * port of Lewis's OpenGL code to X/Motif
264 *
265 * Revision 6.3  1999/09/20 20:12:56  lewisg
266 * change typedefs for a colorcell, add triangle generator, fix incorrect return values
267 *
268 * Revision 6.2  1999/06/14 23:15:11  lewisg
269 * moved useful helper functions out of the ifdef
270 *
271 * Revision 6.1  1999/04/06 14:23:28  lewisg
272 * add opengl replacement for viewer3d
273 *
274 *
275 */
276 
277 #ifdef _OPENGL
278 
279 #if defined(WIN32)              /* braindead windows dependency */
280 #include <windows.h>
281 
282 #elif defined(macintosh)
283 #include <agl.h>
284 #include <fonts.h>
285 # if !defined(OS_UNIX_DARWIN)
286 #include "MoreCarbonAccessors.h"
287 #endif
288 
289 #elif defined(WIN_MOTIF)
290 #include <GL/glx.h>
291 #include <X11/Xlib.h>
292 #ifdef Status   /* avoid name conflict from Xlib */
293 #undef Status
294 #endif
295 
296 #endif
297 
298 /*
299 *  Include the GL dependencies.  GL has its own typedef's for basic types, just like the toolkit.
300 *  If you get warnings about type mismatch, this should be investigated.  The GL typedef's can't
301 *  be included in general toolkit code because of the windows.h dependency for WIN32 which
302 *  causes all sorts of name collisions.
303 */
304 
305 #if defined(macintosh)
306 #include <gl.h>
307 #include <glu.h>
308 #else
309 #include <GL/gl.h>
310 #include <GL/glu.h>
311 #endif
312 
313 #ifdef _PNG
314 #include <png.h> /* must go berore ncbi headers */
315 #endif
316 
317 /* from ncbimisc.h */
318 #include <ncbi.h>
319 NLM_EXTERN void LIBCALL Nlm_HeapSort PROTO((VoidPtr base, size_t nel, size_t width,
320                                            int (LIBCALLBACK *cmp) (VoidPtr, VoidPtr) ));
321 
322 #endif                          /* _OPENGL */
323 
324 #include <math.h>
325 #include <shim3d.h>
326 #include <stdio.h>
327 #include <ddvcolor.h>
328 
329 #if defined(_OPENGL) && defined(_PNG)
330 TOGL_Data *Cn3D_GetCurrentOGLData(void); /* in cn3dxprt.c */
331 #endif
332 
333 
334 /* VRML functions */
335 
336 void VRML_ColorToString(Char * pString, DDV_ColorCell * pColor)
337 {
338     sprintf(pString, "%f %f %f", pColor->rgb[0] / 255.0,
339             pColor->rgb[1] / 255.0, pColor->rgb[2] / 255.0);
340 }
341 
342 void VRML_AddSphere3D(DDV_ColorCell * pColor, FloatHi x, FloatHi y,
343                       FloatHi z, FloatHi radius)
344 {
345     Char szColor[256];
346 
347     printf("Transform {\ntranslation %f %f %f\nchildren [\nShape{\n", x, y,
348            z);
349     printf("appearance Appearance {\nmaterial Material {\n");
350     VRML_ColorToString(szColor, pColor);
351     printf("diffuseColor %s\n}\n}\n", szColor);
352     printf("geometry Sphere {\n radius %f\n}\n", radius);
353     printf("}\n]\n}\n");
354 }
355 
356 void VRML_AddCylinder3D(DDV_ColorCell * pColor,
357                         Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
358                         Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
359                         Nlm_FloatHi radius)
360 {
361     Char szColor[256];
362     FloatHi Rotate[16], Translate[3], length, *Cross;
363     FloatHi z[3] = { 0.0, 0.0, 1.0 };
364 
365     OGL_CreateCTransform(x1, y1, z1, x2, y2, z2, Rotate, Translate,
366                          &length);
367 
368     Cross = OGL_CrossProduct(&Rotate[8], z);
369     if (Cross == NULL)
370         return;
371 
372     printf("Transform {\nrotation %f %f %f %f", Cross[0], Cross[1],
373            Cross[2], acos(Rotate[10]));
374     printf("\ntranslation %f %f %f\nchildren [\nShape{\n", Translate[0],
375            Translate[1], Translate[2]);
376     printf("appearance Appearance {\nmaterial Material {\n");
377     VRML_ColorToString(szColor, pColor);
378     printf("diffuseColor %s\n}\n}\n", szColor);
379     printf("geometry Cylinder {\n radius %f\nheight %f\ntop FALSE\n}\n",
380            radius, length);
381     printf("}\n]\n}\n");
382 
383     MemFree(Cross);
384 }
385 
386 
387 /* function in common with vrml and opengl */
388 
389 FloatHi *OGL_CrossProduct(Nlm_FloatHi * v1, Nlm_FloatHi * v2)
390 {
391     Nlm_FloatHi *RetVal;
392 
393     if (v1 == NULL || v2 == NULL)
394         return NULL;
395     RetVal = MemNew(3 * sizeof(Nlm_FloatHi));
396     if (RetVal == NULL)
397         return NULL;
398 
399     RetVal[0] = v1[1] * v2[2] - v1[2] * v2[1];
400     RetVal[1] = v1[2] * v2[0] - v1[0] * v2[2];
401     RetVal[2] = v1[0] * v2[1] - v1[1] * v2[0];
402 
403     return RetVal;
404 }
405 
406 void OGL_CreateCTransform(Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
407                           Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
408                           Nlm_FloatHi * Rotate, Nlm_FloatHi * Translate,
409                           Nlm_FloatHi * length)
410 {
411     FloatHi a, b, c;            /* the normal z */
412     FloatHi yy2, yy3;           /* the normal y */
413     FloatHi xx1, xx2, xx3;      /* the normal x */
414     Int4 iCount;
415 
416     if (Rotate == NULL || Translate == NULL || length == NULL)
417         return;
418 
419     Translate[0] = (x1 + x2) / 2;
420     Translate[1] = (y1 + y2) / 2;
421     Translate[2] = (z1 + z2) / 2;
422 
423     *length =
424         sqrt(OGL_SQR(x1 - x2) + OGL_SQR(y1 - y2) + OGL_SQR(z1 - z2)) / 2.0;
425 
426     for (iCount = 0; iCount < 16; iCount++)
427         Rotate[iCount] = 0.0;
428     Rotate[15] = 1;             /* identity */
429 
430     /* create the normal z */
431     a = (x1 - Translate[0]) / (*length);
432     b = (y1 - Translate[1]) / (*length);
433     c = (z1 - Translate[2]) / (*length);
434 
435     /* create the normal y */
436 
437     yy2 = sqrt(1.0 / (1.0 + OGL_SQR(b) / OGL_SQR(c)));
438     yy3 = -(b / c) * yy2;
439 
440     /* create the normal x */
441 
442     xx2 =
443         sqrt(1.0 /
444              (pow(c, 4.0) / (OGL_SQR(a) * OGL_SQR(b)) +
445               2.0 * OGL_SQR(c) / OGL_SQR(a)
446               + OGL_SQR(b) / OGL_SQR(a) + 1 + OGL_SQR(c) / OGL_SQR(b)));
447     xx3 = xx2 * c / b;
448     xx1 = (-OGL_SQR(c) / (a * b) - b / a) * xx2;
449 
450 
451     /* now use the normals to make the rotation matrix */
452 
453     Rotate[0] = xx1;
454     Rotate[1] = xx2;
455     Rotate[2] = xx3;
456 
457     Rotate[4] = 0.0;
458     Rotate[5] = yy2;
459     Rotate[6] = yy3;
460 
461     Rotate[8] = a;
462     Rotate[9] = b;
463     Rotate[10] = c;
464 }
465 
466 
467 
468 #ifdef _OPENGL
469 
470 
471 static Nlm_VoidPtr OGL_CurrentName = NULL;
472 
473 
474 
475 /* define this to do (frequent) checking of GL error status - but this is
476    very expensive, so be sure to turn off for production! */
477 /* #define DEBUG_GL 1 */
478 #if defined(_DEBUG) && !defined(DEBUG_GL)
479 #define DEBUG_GL 1
480 #endif
481 
482 /* for now, just print warning if any GL error flag is set */
483 static Boolean OGL_CheckForErrors(void)
484 {
485     GLenum errCode;
486     const GLubyte *errString;
487     Boolean hadErrors = FALSE;
488 
489     while ((errCode = glGetError()) != GL_NO_ERROR) {
490         errString = gluErrorString(errCode);
491         Message(MSG_POST, "OpenGL error: %s", errString);
492         hadErrors = TRUE;
493     }
494     return hadErrors;
495 }
496 
497 /*
498 *   Various helper functions used in drawing
499 */
500 
501 typedef struct _TOGL_Layers
502 /* this struct contains the information used to manage the different layers of the display */
503 {
504     GLuint FirstLayer;
505     GLuint LastLayer;
506     GLuint SelectedLayer;
507     Nlm_Boolean IsOn[OGLMAXLAYERS];
508 } TOGL_Layers;
509 
510 
511 void OGL_Normalize(Nlm_FloatHi * v)
512 /* normalize a vector */
513 {
514     Nlm_FloatHi Length;
515 
516     if (v == NULL)
517         return;
518     Length = sqrt(OGL_SQR(v[0]) + OGL_SQR(v[1]) + OGL_SQR(v[2]));
519     v[0] /= Length;
520     v[1] /= Length;
521     v[2] /= Length;
522 }
523 
524 
525 FloatHi *OGL_MakeNormal(Nlm_FloatHi * origin, Nlm_FloatHi * v1,
526                         Nlm_FloatHi * v2)
527 /* creates a normal to the given 3 vertices */
528 {
529     Nlm_FloatHi Vector1[3], Vector2[3], *RetValue;
530 
531     if (origin == NULL || v1 == NULL || v2 == NULL)
532         return NULL;
533     Vector1[0] = v1[0] - origin[0];
534     Vector1[1] = v1[1] - origin[1];
535     Vector1[2] = v1[2] - origin[2];
536 
537     Vector2[0] = v2[0] - origin[0];
538     Vector2[1] = v2[1] - origin[1];
539     Vector2[2] = v2[2] - origin[2];
540 
541     RetValue = OGL_CrossProduct(Vector1, Vector2);
542     OGL_Normalize(RetValue);
543     return RetValue;
544 }
545 
546 
547 static void ColorCell2Array(GLfloat * array, DDV_ColorCell * color)
548 /* copies a color cell to a GL array */
549 {
550     if (array == NULL || color == NULL)
551         return;
552     array[0] = (GLfloat) (color->rgb[0] / 255.0);
553     array[1] = (GLfloat) (color->rgb[1] / 255.0);
554     array[2] = (GLfloat) (color->rgb[2] / 255.0);
555 }
556 
557 
558 /* these are used for both matrial colors and light colors */
559 static const GLfloat Color_Off[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
560 static const GLfloat Color_MostlyOff[4] = { 0.05f, 0.05f, 0.05f, 1.0f };
561 static const GLfloat Color_MostlyOn[4] = { 0.95f, 0.95f, 0.95f, 1.0f };
562 static const GLfloat Color_On[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
563 
564 /* cache previous color, to avoid unnecessary calls to glMaterial */
565 void OGL_SetColor(TOGL_Data * OGL_Data, DDV_ColorCell * color, GLenum type,
566                   GLfloat alpha)
567 {
568 #ifdef DEBUG_GL
569     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering SetColor");
570 #endif
571 
572     if (!OGL_Data) return;
573     if (OGL_Data->IndexMode == FALSE ) {
574         static GLfloat pr, pg, pb, pa;
575         static GLenum pt = GL_NONE;
576         static GLfloat rgb[4];
577 
578         if (!color) {
579             glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Color_Off);
580             pt = GL_NONE;
581             return;
582         }
583         ColorCell2Array(rgb, color);
584 
585 #ifdef _DEBUG
586         if (rgb[0] == 0.0 && rgb[1] == 0.0 && rgb[2] == 0.0)
587             Message(MSG_POST, "Warning: OGL_Setcolor request color (0,0,0)");
588         if (alpha == 0.0)
589             Message(MSG_POST, "Warning: OGL_SetColor request alpha 0.0");
590 #endif
591 
592         rgb[3] = alpha;
593         if (rgb[0] != pr || rgb[1] != pg || rgb[2] != pb || rgb[3] != pa || type != pt) {
594             if (type != pt) {
595                 if (type == GL_DIFFUSE) {
596                     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, Color_MostlyOff);
597                 } else if (type == GL_AMBIENT) {
598                     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, Color_Off);
599                 } else {
600                     printf("don't know how to handle material type %i\n",type);
601                 }
602                 pt = type;
603             }
604             glMaterialfv(GL_FRONT_AND_BACK, type, rgb);
605             if (type == GL_AMBIENT) {
606                 /* this is necessary so that fonts are rendered in correct
607                    color in SGI's OpenGL implementation, and maybe others */
608                 glColor4f(rgb[0], rgb[1], rgb[2], rgb[3]);
609             }
610             pr = rgb[0];
611             pg = rgb[1];
612             pb = rgb[2];
613             pa = rgb[3];
614         }
615 
616     } else { /* color index mode */
617         ValNodePtr PaletteIndex;
618         GLint indx[3];
619         static GLint pi0 = -1, pi1, pi2;
620         static GLint pt;
621 
622         if (!color) {
623             pi0 = -1;
624             return;
625         }
626 
627         PaletteIndex = OGL_SearchPaletteIndex(OGL_Data->PaletteIndex, color);
628         if (!PaletteIndex) {
629             Message(MSG_POST, "Couldn't find color in PaletteIndex!");
630             return;
631         }
632         if (type == GL_DIFFUSE) {
633             indx[0] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->Begin;
634             indx[1] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
635             indx[2] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
636         } else if (type == GL_AMBIENT) {
637             indx[0] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
638             indx[1] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
639             indx[2] = ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->End;
640         } else {
641             Message(MSG_POST, "don't know how to handle material type %i\n",type);
642         }
643 
644 #ifdef WIN32
645         /* on Windows, need to skip over the first ten static palette colors */
646         indx[0] += 10;
647         indx[1] += 10;
648         indx[2] += 10;
649 #endif
650 
651         if (indx[0] != pi0 || indx[1] != pi1 || indx[2] != pi2 || type != pt) {
652             glMaterialiv(GL_FRONT_AND_BACK, GL_COLOR_INDEXES, indx);
653             pi0 = indx[0];
654             pi1 = indx[1];
655             pi2 = indx[2];
656             pt = type;
657         }
658     }
659 
660 #ifdef DEBUG_GL
661     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving SetColor");
662 #endif
663 }
664 
665 /* only need single once-allocated quadric */
666 static GLUquadricObj *OGL_qobj = NULL;
667 
668 /*
669  *  Functions used to draw various primitives
670  */
671 
672 void OGL_AddQuad3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
673                    Nlm_FloatHi * v1, Nlm_FloatHi * v2, Nlm_FloatHi * v3,
674                    Nlm_FloatHi * v4)
675 /* draws a quadralateral with the 4 given vertices of form double v1[3] */
676 {
677     Nlm_FloatHi *Normal;
678 
679     if (v1 == NULL || v2 == NULL || v3 == NULL || v4 == NULL
680         || OGL_Data == NULL || color == NULL)
681         return;
682 
683     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
684 
685     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);*/
686 
687     glBegin(GL_QUADS);
688     Normal = OGL_MakeNormal(v1, v2, v4);
689     if (Normal != NULL) {
690         glNormal3dv(Normal);
691         MemFree(Normal);
692     }
693     glVertex3dv(v1);
694     glVertex3dv(v2);
695     glVertex3dv(v3);
696     glVertex3dv(v4);
697     glEnd();
698 
699     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);*/
700 }
701 
702 void OGL_AddBrick3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
703                     Nlm_FloatHi * Nterm, Nlm_FloatHi * Cterm,
704                     Nlm_FloatHi * norm, Nlm_FloatHi width,
705                     Nlm_FloatHi thickness, Nlm_Boolean doArrow)
706 {
707     static const double arrowLen = 2.8, arrowWidthProp = 1.6;
708     
709     GLdouble c000[3], c001[3], c010[3], c011[3],
710              c100[3], c101[3], c110[3], c111[3], n[3];
711     Nlm_FloatHi a[3], *h;
712     int i;
713 
714 #ifdef DEBUG_GL
715     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddBrick");
716 #endif
717 
718     if (Nterm == NULL || Cterm == NULL || norm == NULL ||
719         OGL_Data == NULL || color == NULL ||
720         width*thickness == 0.0)
721         return;
722 
723     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
724 
725     /* in this brick's world coordinates, the long axis (N-C direction) is
726        along +Z, with N terminus at Z=0; width is in the X direction, and
727        thickness in Y. Arrowhead at C-terminus, of course. */
728            
729     OGL_Normalize(norm);
730 
731     for (i=0; i<3; i++) {
732         a[i] = Cterm[i] - Nterm[i];
733     }
734     OGL_Normalize(a);
735     h = OGL_CrossProduct(norm, a);
736     if (!h) return;
737 
738     if (doArrow)
739         for (i=0; i<3; i++)
740             Cterm[i] -= a[i] * arrowLen;
741             
742     for (i=0; i<3; i++) {
743         c000[i] = Nterm[i] - h[i]*width/2 - norm[i]*thickness/2;
744         c001[i] = Cterm[i] - h[i]*width/2 - norm[i]*thickness/2;
745         c010[i] = Nterm[i] - h[i]*width/2 + norm[i]*thickness/2;
746         c011[i] = Cterm[i] - h[i]*width/2 + norm[i]*thickness/2;
747         c100[i] = Nterm[i] + h[i]*width/2 - norm[i]*thickness/2;
748         c101[i] = Cterm[i] + h[i]*width/2 - norm[i]*thickness/2;
749         c110[i] = Nterm[i] + h[i]*width/2 + norm[i]*thickness/2;
750         c111[i] = Cterm[i] + h[i]*width/2 + norm[i]*thickness/2;
751     }
752 
753     glBegin(GL_QUADS);
754 
755     for (i=0; i<3; i++) n[i] = norm[i];
756     glNormal3dv(n);
757     glVertex3dv(c010);
758     glVertex3dv(c011);
759     glVertex3dv(c111);
760     glVertex3dv(c110);
761 
762     for (i=0; i<3; i++) n[i] = -norm[i];
763     glNormal3dv(n);
764     glVertex3dv(c000);
765     glVertex3dv(c100);
766     glVertex3dv(c101);
767     glVertex3dv(c001);
768 
769     for (i=0; i<3; i++) n[i] = h[i];
770     glNormal3dv(n);
771     glVertex3dv(c100);
772     glVertex3dv(c110);
773     glVertex3dv(c111);
774     glVertex3dv(c101);
775 
776     for (i=0; i<3; i++) n[i] = -h[i];
777     glNormal3dv(n);
778     glVertex3dv(c000);
779     glVertex3dv(c001);
780     glVertex3dv(c011);
781     glVertex3dv(c010);
782 
783     for (i=0; i<3; i++) n[i] = -a[i];
784     glNormal3dv(n);
785     glVertex3dv(c000);
786     glVertex3dv(c010);
787     glVertex3dv(c110);
788     glVertex3dv(c100);
789 
790     if (!doArrow) {
791         for (i=0; i<3; i++) n[i] = a[i];
792         glNormal3dv(n);
793         glVertex3dv(c001);
794         glVertex3dv(c101);
795         glVertex3dv(c111);
796         glVertex3dv(c011);
797 
798     } else {
799         GLdouble FT[3], LT[3], RT[3], FB[3], LB[3], RB[3];
800         Nlm_FloatHi *nL, *nR;
801     
802         for (i=0; i<3; i++) {
803             FT[i] = Cterm[i] + norm[i]*thickness/2 + a[i]*arrowLen;
804             LT[i] = Cterm[i] + norm[i]*thickness/2 + h[i]*arrowWidthProp*width/2;
805             RT[i] = Cterm[i] + norm[i]*thickness/2 - h[i]*arrowWidthProp*width/2;
806             FB[i] = Cterm[i] - norm[i]*thickness/2 + a[i]*arrowLen;
807             LB[i] = Cterm[i] - norm[i]*thickness/2 + h[i]*arrowWidthProp*width/2;
808             RB[i] = Cterm[i] - norm[i]*thickness/2 - h[i]*arrowWidthProp*width/2;
809         }
810 
811         for (i=0; i<3; i++) n[i] = -a[i];
812         glNormal3dv(n);
813         glVertex3dv(c111);
814         glVertex3dv(LT);
815         glVertex3dv(LB);
816         glVertex3dv(c101);
817 
818         glVertex3dv(c011);
819         glVertex3dv(c001);
820         glVertex3dv(RB);
821         glVertex3dv(RT);
822 
823         for (i=0; i<3; i++) h[i] = FT[i] - LT[i];
824         if (!(nL = OGL_CrossProduct(norm, h))) return;
825         OGL_Normalize(nL);
826         for (i=0; i<3; i++) n[i] = nL[i];
827         glNormal3dv(n);
828         glVertex3dv(FT);
829         glVertex3dv(FB);
830         glVertex3dv(LB);
831         glVertex3dv(LT);
832         MemFree(nL);
833 
834         for (i=0; i<3; i++) h[i] = FT[i] - RT[i];
835         if (!(nR = OGL_CrossProduct(h, norm))) return;
836         OGL_Normalize(nR);
837         for (i=0; i<3; i++) n[i] = nR[i];
838         glNormal3dv(n);
839         glVertex3dv(FT);
840         glVertex3dv(RT);
841         glVertex3dv(RB);
842         glVertex3dv(FB);
843         MemFree(nR);
844 
845         glEnd();
846         glBegin(GL_TRIANGLES);
847         
848         for (i=0; i<3; i++) n[i] = norm[i];
849         glNormal3dv(n);
850         glVertex3dv(FT);
851         glVertex3dv(LT);
852         glVertex3dv(RT);
853         
854         for (i=0; i<3; i++) n[i] = -norm[i];
855         glNormal3dv(n);
856         glVertex3dv(FB);
857         glVertex3dv(RB);
858         glVertex3dv(LB);
859     }
860 
861     glEnd();
862 
863     MemFree(h);
864 
865 #ifdef DEBUG_GL
866     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddBrick");
867 #endif
868 }
869 
870 void OGL_AddTri3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
871                   Nlm_FloatHi * v1, Nlm_FloatHi * v2, Nlm_FloatHi * v3,
872                   Nlm_FloatHi * Normal)
873 /* draws a triangle given 3 vertices of form double v1[3] and the normal */
874 {
875     if (v1 == NULL || v2 == NULL || v3 == NULL || OGL_Data == NULL ||
876         color == NULL)
877         return;
878 
879     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
880 
881     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);*/
882 
883     glBegin(GL_TRIANGLES);
884     if (Normal != NULL)
885         glNormal3dv(Normal);
886     glVertex3dv(v1);
887     glVertex3dv(v2);
888     glVertex3dv(v3);
889     glEnd();
890 
891     /*glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);*/
892 }
893 
894 
895 void OGL_AddCylinder3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
896                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1, Nlm_Boolean cap1,
897                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2, Nlm_Boolean cap2,
898                        Nlm_FloatHi radius, Nlm_Int4 sides, Nlm_Boolean doArrow)
899                         /* create a cylinder with given endcaps and radius */
900 {
901     static const double arrowLen = 4.0,
902         arrowWidthPropBase = 1.2, arrowWidthPropTip = 0.4;
903 
904     Nlm_Int4 iCount;
905     GLdouble length;
906 
907 #ifdef DEBUG_GL
908     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddCylinder");
909 #endif
910 
911     if (OGL_Data == NULL || color == NULL)
912         return;
913 
914     OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
915 
916     glPushMatrix();
917 
918     length = sqrt(
919         (x2 - x1) * (x2 - x1) +
920         (y2 - y1) * (y2 - y1) +
921         (z2 - z1) * (z2 - z1)
922     );
923     if (length < 0.000001) return;
924 
925     /* to translate into place */
926     glTranslated(x1, y1, z1);
927 
928     /* to rotate from initial position, so bond points right direction;
929        handle special case where both ends share ~same x,y */
930 #define DEGREES(rad) ((rad)*180.0/3.14159265358979323846)
931     if (fabs(y1 - y2) < 0.000001 &&
932         fabs(x2 - x1) < 0.000001) {
933         if (z2 - z1 < 0.0) glRotated(180.0,1.0,0.0,0.0);
934     } else {
935         glRotated(DEGREES(acos((z2 - z1) / length)),
936                   y1 - y2, x2 - x1, 0.0);
937     }
938 
939     if (doArrow) length -= arrowLen;
940     gluCylinder(OGL_qobj, radius, radius, length, sides, 1);
941 
942     if (cap1) {
943         glPushMatrix();
944         glRotated(180.0, 0.0, 1.0, 0.0);
945         gluDisk(OGL_qobj, 0.0, radius, sides, 1);
946         glPopMatrix();
947     }
948     if (doArrow) {
949         glPushMatrix();
950         glTranslated(0.0, 0.0, length);
951         if (arrowWidthPropBase > 1.0) {
952             glPushMatrix();
953             glRotated(180.0, 0.0, 1.0, 0.0);
954             gluDisk(OGL_qobj, 0.0, radius*arrowWidthPropBase, sides, 1);
955             glPopMatrix();
956         }
957         gluCylinder(OGL_qobj, radius*arrowWidthPropBase,
958             radius*arrowWidthPropTip, arrowLen, sides, 10);
959         if (arrowWidthPropTip > 0.0) {
960             glTranslated(0.0, 0.0, arrowLen);
961             gluDisk(OGL_qobj, 0.0, radius*arrowWidthPropTip, sides, 1);
962         }
963         glPopMatrix();
964     } else if (cap2) {
965         glPushMatrix();
966         glTranslated(0.0, 0.0, length);
967         gluDisk(OGL_qobj, 0.0, radius, sides, 1);
968         glPopMatrix();
969     }
970 
971     glPopMatrix();
972 
973 #ifdef DEBUG_GL
974     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddCylinder");
975 #endif
976 }
977 
978 
979 void OGL_AddLine3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
980                    Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
981                    Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2)
982                     /* draw a single line */
983 {
984 #ifdef DEBUG_GL
985     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddLine");
986 #endif
987 
988     if (OGL_Data == NULL || color == NULL)
989         return;
990 
991     OGL_SetColor(OGL_Data, color, GL_AMBIENT, 1.0);
992 
993     glBegin(GL_LINES);
994     glVertex3d(x1, y1, z1);
995     glVertex3d(x2, y2, z2);
996     glEnd();
997 
998 #ifdef DEBUG_GL
999     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddLine");
1000 #endif
1001 }
1002 
1003 
1004 typedef struct _TransparentSphereData {
1005     DDV_ColorCell color;
1006     Nlm_FloatHi x, y, z, radius, alpha, distFromCamera;
1007     Nlm_Int4 slices, stacks;
1008     Nlm_Int1 layer;
1009     Nlm_VoidPtr name;
1010     ValNodePtr transforms;
1011     struct _TransparentSphereData *next;
1012 } TransparentSphereData, PNTR TransparentSphereDataPtr;
1013 
1014 static TransparentSphereDataPtr OGL_transSpheresTail = NULL,
1015                                 OGL_transSpheresHead = NULL,
1016                                 *OGL_transSpheresList = NULL;
1017 
1018 int LIBCALLBACK OGL_DistCompareFunc(Nlm_VoidPtr va, Nlm_VoidPtr vb)
1019 {
1020     TransparentSphereDataPtr a = *((TransparentSphereDataPtr *) va),
1021                              b = *((TransparentSphereDataPtr *) vb);
1022     
1023     if (a->distFromCamera > b->distFromCamera) return -1;
1024     else if (a->distFromCamera < b->distFromCamera) return 1;
1025         else return 0;
1026 }
1027 
1028 Nlm_Boolean OGL_GetLayer(TOGL_Data *, Nlm_Int4);
1029 
1030 static void OGL_RenderTransparentSpheres(TOGL_Data *OGL_Data)
1031 {
1032 #ifdef DEBUG_GL
1033     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_RenderTransparentSpheres");
1034 #endif
1035 
1036     if (OGL_Data && OGL_transSpheresHead) {
1037         TransparentSphereDataPtr sph;
1038         int i, n, iList;
1039         GLdouble m[16];
1040         Nlm_FloatHi x, y, z;
1041         Nlm_Boolean show;
1042 
1043         /* make an array of pointers to sphere data; sort by distance from camera */
1044         for (n=0, sph=OGL_transSpheresHead; sph; n++, sph = sph->next) {
1045 
1046             /* transform model's xyz into GL-frame coordinates */
1047             OGL_PushTransformation(sph->transforms);
1048             glGetDoublev(GL_MODELVIEW_MATRIX, m);
1049             OGL_PopTransformation();
1050 
1051             x = m[0]*sph->x + m[4]*sph->y + m[8]*sph->z + m[12];
1052             y = m[1]*sph->x + m[5]*sph->y + m[9]*sph->z + m[13];
1053             z = m[2]*sph->x + m[6]*sph->y + m[10]*sph->z + m[14];
1054             sph->distFromCamera =
1055                 sqrt((x * x) + (y * y) +
1056                      ((z - OGL_Data->CameraDistance) * 
1057                       (z - OGL_Data->CameraDistance)))
1058                 - sph->radius;
1059         }
1060 
1061         if (!OGL_transSpheresList) {
1062             OGL_transSpheresList = (TransparentSphereDataPtr *)
1063                 MemNew(n * sizeof(TransparentSphereDataPtr));
1064             if (!OGL_transSpheresList) return;
1065             for (n=0, sph=OGL_transSpheresHead; sph; n++, sph=sph->next)
1066                 OGL_transSpheresList[n] = sph;
1067         }
1068         Nlm_HeapSort((Nlm_VoidPtr) OGL_transSpheresList, n,
1069                      sizeof(TransparentSphereDataPtr), OGL_DistCompareFunc);
1070 
1071         /* turn on blending */
1072         glEnable(GL_BLEND);
1073         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1074 
1075         /* render the spheres in order, and only for turned-on layers */
1076         for (i = 0; i < n; i++) {
1077             show = FALSE;
1078             if (OGL_Data->Layers->SelectedLayer) {
1079                 if (OGL_Data->Layers->SelectedLayer - OGL_Data->Layers->FirstLayer == 
1080                     OGL_transSpheresList[i]->layer) {
1081                     show = TRUE;
1082                 }
1083             } else {
1084                 for (iList = OGL_Data->Layers->FirstLayer;
1085                      iList <= OGL_Data->Layers->LastLayer;
1086                      iList++) {
1087                     if (iList - OGL_Data->Layers->FirstLayer == OGL_transSpheresList[i]->layer &&
1088                         OGL_GetLayer(OGL_Data, iList - OGL_Data->Layers->FirstLayer)) {
1089                         show = TRUE;
1090                         break;
1091                     }
1092                 }
1093             }
1094             if (show) {
1095                 OGL_LoadName(OGL_transSpheresList[i]->name);
1096                 OGL_SetColor(OGL_Data, &(OGL_transSpheresList[i]->color),
1097                              GL_DIFFUSE, OGL_transSpheresList[i]->alpha);
1098                 OGL_PushTransformation(OGL_transSpheresList[i]->transforms);
1099                 glTranslated(OGL_transSpheresList[i]->x, OGL_transSpheresList[i]->y,
1100                              OGL_transSpheresList[i]->z);
1101                 gluSphere(OGL_qobj, OGL_transSpheresList[i]->radius,
1102                           OGL_transSpheresList[i]->slices, OGL_transSpheresList[i]->stacks);
1103                 OGL_PopTransformation();
1104             }
1105         }
1106 
1107         /* blending back off now */
1108         glDisable(GL_BLEND);
1109     }
1110 
1111 #ifdef DEBUG_GL
1112     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_RenderTransparentSpheres");
1113 #endif
1114 }
1115 
1116 static Nlm_Int1 OGL_currentLayer;
1117 
1118 static void OGL_AddTransparentSphere(DDV_ColorCell * color,
1119                      Nlm_FloatHi x, Nlm_FloatHi y, Nlm_FloatHi z,
1120                      Nlm_FloatHi radius, Nlm_Int4 slices, Nlm_Int4 stacks,
1121                      Nlm_FloatHi alpha, Nlm_VoidPtr name, ValNodePtr transforms)
1122 {
1123     TransparentSphereDataPtr newSphere = (TransparentSphereDataPtr)
1124         MemNew(sizeof(TransparentSphereData));
1125     if (newSphere) {
1126         DDV_CopyColorCell(&(newSphere->color), color);
1127         newSphere->x = x;
1128         newSphere->y = y;
1129         newSphere->z = z;
1130         newSphere->transforms = transforms;
1131         newSphere->radius = radius;
1132         newSphere->slices = slices;
1133         newSphere->stacks = stacks;
1134         newSphere->alpha = alpha;
1135         newSphere->layer = OGL_currentLayer;
1136         newSphere->name = name;
1137         newSphere->next = NULL;
1138         if (OGL_transSpheresTail)
1139             OGL_transSpheresTail = OGL_transSpheresTail->next = newSphere;
1140         else
1141             OGL_transSpheresTail = OGL_transSpheresHead = newSphere;
1142     }
1143 }
1144 
1145 void OGL_ClearTransparentSpheres(void)
1146 {
1147     TransparentSphereDataPtr sph = OGL_transSpheresHead, tmp;
1148     
1149     while (sph) {
1150         tmp = sph->next;
1151         MemFree(sph);
1152         sph = tmp;
1153     }
1154     OGL_transSpheresHead = OGL_transSpheresTail = NULL;
1155     if (OGL_transSpheresList) {
1156         MemFree(OGL_transSpheresList);
1157         OGL_transSpheresList = NULL;
1158     }
1159 }
1160 
1161 
1162 void OGL_AddSphere3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
1163                      Nlm_FloatHi x, Nlm_FloatHi y, Nlm_FloatHi z,
1164                      Nlm_FloatHi radius, Nlm_Int4 slices, Nlm_Int4 stacks,
1165                      Nlm_FloatHi alpha, ValNodePtr transforms)
1166                       /* draws a sphere */
1167 {
1168 #ifdef DEBUG_GL
1169     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering AddSphere");
1170 #endif
1171 
1172     if (OGL_Data == NULL || color == NULL)
1173         return;
1174 
1175     if(!OGL_Data->IndexMode && alpha < 1.0) /* no transparency in index mode */
1176         OGL_AddTransparentSphere(color, x, y, z, radius, slices, stacks,
1177                                  alpha, OGL_CurrentName, transforms);
1178     else {
1179         OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
1180         glPushMatrix();
1181         glTranslated(x, y, z);
1182         gluSphere(OGL_qobj, radius, slices, stacks);
1183         glPopMatrix();
1184     }
1185 
1186 #ifdef DEBUG_GL
1187     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving AddSphere");
1188 #endif
1189 }
1190 
1191 
1192 void OGL_AddText3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
1193                    Nlm_CharPtr string, Nlm_FloatHi x, Nlm_FloatHi y,
1194                    Nlm_FloatHi z, Nlm_Int2 flags)
1195 {
1196     Nlm_Int4 Length, i;
1197 
1198     if (OGL_Data == NULL || color == NULL || string == NULL)
1199         return;
1200 
1201     OGL_SetColor(OGL_Data, color, GL_AMBIENT, 1.0);
1202 
1203     glListBase(OGLFONTBASE);
1204 
1205     Length = Nlm_StrLen(string);
1206     glRasterPos3d(x, y, z);
1207     if (flags & OGLTEXT3D_CENTER) {
1208         glBitmap(0, 0, 0.0, 0.0,
1209                  (GLfloat) -0.5 * Length * OGL_Data->SpaceWidth, 0.0,
1210                  NULL);
1211     }
1212     if (flags & OGLTEXT3D_MIDDLE)
1213         glBitmap(0, 0, 0.0, 0.0,
1214                  0.0f, (GLfloat) (-0.5 * OGL_Data->SpaceHeight),
1215                  NULL);
1216 
1217     glCallLists(Length, GL_UNSIGNED_BYTE, string);
1218 
1219     glListBase(0);
1220 }
1221 
1222 
1223 void OGL_PushTransformation(ValNodePtr transforms)
1224 {
1225     FloatLoPtr pflv;
1226     FloatLoPtr *ppflm;
1227     GLfloat xmat[16];
1228 
1229     glPushMatrix();
1230 
1231     while (transforms) {
1232         if (transforms->choice == 2) { /* Move_translate */
1233             pflv = (FloatLoPtr) transforms->data.ptrvalue;
1234             glTranslatef(pflv[0], pflv[1], pflv[2]);
1235 
1236         } else if (transforms->choice == 1) { /* Move_rotate */
1237             ppflm = (FloatLoPtr *) transforms->data.ptrvalue;
1238             xmat[0]=ppflm[0][0]; xmat[4]=ppflm[1][0]; xmat[8]= ppflm[2][0]; xmat[12]=0;
1239             xmat[1]=ppflm[0][1]; xmat[5]=ppflm[1][1]; xmat[9]= ppflm[2][1]; xmat[13]=0;
1240             xmat[2]=ppflm[0][2]; xmat[6]=ppflm[1][2]; xmat[10]=ppflm[2][2]; xmat[14]=0;
1241             xmat[3]=0;           xmat[7]=0;           xmat[11]=0;           xmat[15]=1;
1242             glMultMatrixf(xmat);
1243         }
1244 
1245         transforms = transforms->next;
1246     }
1247 }
1248 
1249 void OGL_PopTransformation(void)
1250 {
1251     glPopMatrix();
1252 }
1253 
1254 /*
1255 *   Functions used to manage display lists
1256 */
1257 
1258 void OGL_Start(TOGL_Data * OGL_Data, Nlm_Int1 List)
1259 {
1260 #ifdef DEBUG_GL
1261     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_Start");
1262 #endif
1263 
1264     if (OGL_Data == NULL)
1265         return;
1266 
1267     /* begin a display list */
1268     if (List >= OGLMAXLAYERS)
1269         return;
1270     glNewList(List + OGL_Data->Layers->FirstLayer, GL_COMPILE);
1271     OGL_SetLayer(OGL_Data, List, TRUE);
1272     OGL_currentLayer = List;
1273 
1274     /* clear all color states */
1275     OGL_SetColor(OGL_Data, NULL, GL_NONE, 1.0);
1276 
1277 #ifdef DEBUG_GL
1278     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_Start");
1279 #endif
1280 }
1281 
1282 void OGL_End()
1283 /* end a display list */
1284 {
1285 #ifdef DEBUG_GL
1286     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_End");
1287 #endif
1288     glEndList();
1289 #ifdef DEBUG_GL
1290     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_End");
1291 #endif
1292 }
1293 
1294 
1295 void OGL_SetLayers(TOGL_Data * OGL_Data, Nlm_Boolean Status)
1296 /* set the status of all the layers */
1297 {
1298     Nlm_Int4 i;
1299 
1300     if (OGL_Data == NULL)
1301         return;
1302     for (i = 0; i < OGLMAXLAYERS; i++)
1303         OGL_Data->Layers->IsOn[i] = Status;
1304     return;
1305 }
1306 
1307 void OGL_SetLayer(TOGL_Data * OGL_Data, Nlm_Int4 i, Nlm_Boolean Status)
1308 /* set the status of a particular layer -- is it on or off? */
1309 {
1310     if (OGL_Data == NULL)
1311         return;
1312     OGL_Data->Layers->IsOn[i] = Status;
1313     return;
1314 }
1315 
1316 Nlm_Boolean OGL_GetLayer(TOGL_Data * OGL_Data, Nlm_Int4 i)
1317 /* return layer status */
1318 {
1319     if (OGL_Data == NULL)
1320         return FALSE;
1321     return OGL_Data->Layers->IsOn[i];
1322 }
1323 
1324 
1325 void OGL_SetLayerTop3D(TOGL_Data * OGL_Data, Nlm_Int4 TopLayer)
1326 /* set the highest value layer used */
1327 {
1328     if (OGL_Data == NULL)
1329         return;
1330     OGL_Data->Layers->LastLayer = TopLayer + OGL_Data->Layers->FirstLayer;
1331     return;
1332 }
1333 
1334 void OGL_AllLayerOnProc(TOGL_Data * OGL_Data)
1335 /* turn on all used layers */
1336 {
1337     if (OGL_Data == NULL)
1338         return;
1339     OGL_Data->Layers->SelectedLayer = 0;
1340     OGL_SetLayers(OGL_Data, TRUE);
1341     return;
1342 }
1343 
1344 void OGL_RewindLayerProc(TOGL_Data * OGL_Data)
1345 /* rewind to the first layer */
1346 {
1347     if (OGL_Data == NULL)
1348         return;
1349     OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1350     return;
1351 }
1352 
1353 
1354 void OGL_PrevLayerProc(TOGL_Data * OGL_Data)
1355 /* go back to the previous layer */
1356 {
1357     if (OGL_Data == NULL)
1358         return;
1359     if (OGL_Data->Layers->SelectedLayer) {
1360         if (OGL_Data->Layers->SelectedLayer > OGL_Data->Layers->FirstLayer)
1361             OGL_Data->Layers->SelectedLayer--;
1362         else
1363             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->LastLayer;
1364     } else
1365         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1366 
1367     return;
1368 }
1369 
1370 
1371 void OGL_NextLayerProc(TOGL_Data * OGL_Data)
1372 /* go to the next layer */
1373 {
1374     if (OGL_Data == NULL)
1375         return;
1376     if (OGL_Data->Layers->SelectedLayer) {
1377         if (OGL_Data->Layers->SelectedLayer < OGL_Data->Layers->LastLayer)
1378             OGL_Data->Layers->SelectedLayer++;
1379         else
1380             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1381     } else
1382         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1383 
1384     return;
1385 }
1386 
1387 
1388 void OGL_Play(TOGL_Data * OGL_Data)
1389 /* used to flip through layers in endless loop */
1390 {
1391     if (OGL_Data == NULL)
1392         return;
1393     if (OGL_Data->Layers->SelectedLayer) {
1394         if (OGL_Data->Layers->SelectedLayer < OGL_Data->Layers->LastLayer)
1395             OGL_Data->Layers->SelectedLayer++;
1396         else
1397             OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1398     } else
1399         OGL_Data->Layers->SelectedLayer = OGL_Data->Layers->FirstLayer;
1400 
1401     return;
1402 }
1403 
1404 
1405 /*
1406  *  Color manipulation functions
1407  */
1408 
1409 
1410 ValNodePtr OGL_SearchPaletteIndex(ValNodePtr PaletteIndex,
1411                                   DDV_ColorCell * pColorCell)
1412 {
1413     if (PaletteIndex == NULL || pColorCell == NULL)
1414         return NULL;
1415     for (; PaletteIndex; PaletteIndex = PaletteIndex->next)
1416         if (((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1417             ColorCell.rgb[0] == pColorCell->rgb[0]
1418             && ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1419             ColorCell.rgb[1] == pColorCell->rgb[1]
1420             && ((TOGL_PaletteIndex *) (PaletteIndex->data.ptrvalue))->
1421             ColorCell.rgb[2] == pColorCell->rgb[2]) {
1422             return PaletteIndex;
1423         }
1424     return NULL;
1425 }
1426 
1427 
1428 /*
1429  *  The mouse and rotation/translation/zoom code
1430  */
1431 
1432 static TOGL_Data *MAToOGL(MAPtr ma)
1433 /* extracts OGL_Data out of the extra pointer in the mouse data */
1434 {
1435     return (TOGL_Data *) MA_GetExtra(ma);
1436 }
1437 
1438 
1439 static TOGL_Data *PanelToOGL(Nlm_PaneL panel)
1440 /* extract OGL_Data out of the panel data */
1441 {
1442     MAPtr ma;
1443     Nlm_GetPanelExtra(panel, &ma);
1444 
1445     return MAToOGL(ma);
1446 }
1447 
1448 
1449 static void OGL_DrawViewer3D_CB(Nlm_PaneL panel)
1450 /* callback */
1451 {
1452     OGL_DrawViewer3D(PanelToOGL(panel));
1453 }
1454 
1455 
1456 /*
1457  *  Move
1458  */
1459 static void OGL_Move3D(TOGL_Data * OGL_Data, Nlm_Int2 dx, Nlm_Int2 dy)
1460 {
1461     GLint viewport[4];
1462     Nlm_FloatHi pixelSize;
1463 
1464     glGetIntegerv(GL_VIEWPORT, viewport);
1465 
1466     pixelSize =
1467         tan(OGL_Data->CameraAngle / 2.0) *
1468         2.0 * OGL_Data->CameraDistance /
1469         viewport[3];
1470 
1471     OGL_Data->CameraDirection[0] -= dx * pixelSize;
1472     OGL_Data->CameraDirection[1] += dy * pixelSize;
1473 
1474     OGL_Data->NeedCameraSetup = TRUE;
1475 }
1476 
1477 
1478 /*
1479  *  Zoom
1480  */
1481 extern void Nlm_GetRect (Nlm_GraphiC a, Nlm_RectPtr r);
1482 
1483 static void OGL_Zoom3D(TOGL_Data * OGL_Data, Nlm_Int2 x1, Nlm_Int2 y1,
1484                        Nlm_Int2 x2, Nlm_Int2 y2)
1485 {
1486     Nlm_FloatHi zoom;
1487     Nlm_RecT rect;
1488     GLint viewport[4];
1489 
1490     /* translate window coords as passed to this function into coords
1491        relative to OpenGL area (the Panel) coords */
1492     Nlm_GetRect((Nlm_GraphiC)OGL_Data->Panel, &rect);
1493     x1 -= rect.left;
1494     x2 -= rect.left;
1495     y1 -= rect.top;
1496     y2 -= rect.top;
1497 
1498     /* set new camera direction ... */
1499     glGetIntegerv(GL_VIEWPORT, viewport);
1500     OGL_Move3D(OGL_Data,
1501         (viewport[2] - (x1 + x2)) / 2,
1502         (viewport[3] - (y1 + y2)) / 2);
1503 
1504     /* ... and angle. (This assumes zoom box aspect ratio is correct!) */
1505     zoom = ((Nlm_FloatHi) abs(y1 - y2)) / viewport[3];
1506     OGL_Data->CameraAngle = atan(zoom * tan(OGL_Data->CameraAngle));
1507 
1508     OGL_Data->NeedCameraSetup = TRUE;
1509 }
1510 
1511 NLM_EXTERN void OGL_ZoomOut(TOGL_Data *OGL_Data)
1512 {
1513     OGL_Data->CameraAngle *= 1.5;
1514     OGL_Data->NeedCameraSetup = TRUE;
1515     OGL_DrawViewer3D(OGL_Data);
1516 }
1517 
1518 NLM_EXTERN void OGL_ZoomIn(TOGL_Data *OGL_Data)
1519 {
1520     OGL_Data->CameraAngle /= 1.5;
1521     OGL_Data->NeedCameraSetup = TRUE;
1522     OGL_DrawViewer3D(OGL_Data);
1523 }
1524 
1525 /*
1526  *  Rotation
1527  */
1528 
1529 
1530 typedef enum {
1531     ROTATE_X,
1532     ROTATE_Y,
1533     ROTATE_Z
1534 } OGL_enumRotate3D;
1535 
1536 typedef struct {
1537     OGL_enumRotate3D H;         /* horizontal dragging */
1538     OGL_enumRotate3D V;         /* vertical   dragging */
1539 } OGL_RotatePivots3D, *OGL_RotatePivots3DPtr;
1540 
1541 
1542 static void OGL_Rotate(TOGL_Data * OGL_Data, Nlm_Int4 dAngle,
1543                        OGL_enumRotate3D pivot)
1544 {
1545 #ifdef DEBUG_GL
1546     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering OGL_Rotate");
1547 #endif
1548 
1549     if (!dAngle)
1550         return;
1551     if (OGL_Data == NULL)
1552         return;
1553 
1554     glLoadIdentity();
1555 
1556     switch (pivot) {
1557     case ROTATE_X:
1558         glRotatef((GLfloat) (dAngle), 1.0f, 0.0f, 0.0f);
1559         break;
1560     case ROTATE_Y:
1561         glRotatef((GLfloat) (dAngle), 0.0f, 1.0f, 0.0f);
1562         break;
1563     case ROTATE_Z:
1564         glRotatef((GLfloat) (dAngle), 0.0f, 0.0f, 1.0f);
1565         break;
1566     }
1567 
1568     glMultMatrixd((GLdouble *) OGL_Data->ModelMatrix);
1569 
1570     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
1571 
1572 #ifdef DEBUG_GL
1573     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving OGL_Rotate");
1574 #endif
1575 }
1576 
1577 
1578 
1579 static void OGL_ViewerRotate(Nlm_SlatE panel, Nlm_Int4 delta,
1580                              OGL_enumRotate3D pivot,
1581                              Nlm_Boolean adjust_scrollbar,
1582                              Nlm_Boolean redraw)
1583 {
1584     TOGL_Data *OGL_Data;
1585 
1586     if (panel == NULL ||
1587         !Nlm_Visible(panel) || !Nlm_AllParentsVisible(panel)) return;
1588 
1589     OGL_Data = PanelToOGL((Nlm_PaneL) panel);
1590 
1591     if (adjust_scrollbar) {     /* adjust the relevant rotation scrollbar, if any */
1592         Nlm_BaR sbar = NULL;
1593         switch (pivot) {
1594         case ROTATE_X:
1595             sbar = Nlm_GetSlateVScrollBar(panel);
1596             break;
1597         case ROTATE_Y:
1598             sbar = Nlm_GetSlateHScrollBar(panel);
1599             break;
1600         case ROTATE_Z:
1601             sbar = OGL_Data->Z_rotate;
1602             break;
1603         }
1604 
1605         if (sbar) {
1606             Nlm_ResetClip();
1607             Nlm_CorrectBarValue(sbar,
1608                                 (Nlm_GetValue(sbar) + delta + 360) % 360);
1609         }
1610     }
1611 
1612     /* the coordination transformation (rotation) */
1613     if (pivot == ROTATE_X)
1614         delta = -delta;
1615     OGL_Rotate(OGL_Data, delta, pivot);
1616 
1617     if (redraw) {               /* Draw the viewer */
1618         OGL_DrawViewer3D(OGL_Data);
1619     }
1620 
1621     /*  Nlm_DiagReset(); */
1622 }
1623 
1624 
1625 
1626 /* scrollbar callbacks */
1627 static void OGL_ViewerVScrollProc(Nlm_BaR sb, Nlm_SlatE viewer,
1628                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1629 {
1630     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_X, FALSE, TRUE);
1631 }
1632 
1633 static void OGL_ViewerHScrollProc(Nlm_BaR sb, Nlm_SlatE viewer,
1634                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1635 {
1636     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_Y, FALSE, TRUE);
1637 }
1638 
1639 static void OGL_ViewerZScrollProc(Nlm_BaR sb, Nlm_GraphiC group,
1640                                   Nlm_Int2 newval, Nlm_Int2 oldval)
1641 {
1642     Nlm_SlatE viewer = (Nlm_SlatE) Nlm_GetObjectExtra(sb);
1643     OGL_ViewerRotate(viewer, newval - oldval, ROTATE_Z, FALSE, TRUE);
1644 }
1645 
1646 
1647 
1648 
1649 /*
1650  *  MOUSE EVENT HANDLERS
1651  */
1652 
1653 
1654 
1655 /*
1656  * MOVE
1657  */
1658 
1659 static void OGL_Move_DrawTrace(MA_TracePtr trace)
1660 {
1661     if (Nlm_EqualPt(trace->start, trace->end))
1662         return;
1663 
1664 #ifdef WIN_MOTIF
1665     Nlm_SetColor(0xf1);
1666 #endif
1667     Nlm_InvertMode();           /* turn on xor */
1668     Nlm_DrawLine(trace->start, trace->end); /* draw to current hdc */
1669     Nlm_CopyMode();             /* turn off xor */
1670 }
1671 
1672 
1673 static void OGL_Move_PressMA(MAPtr ma,
1674                              MA_TracePtr trace, Nlm_PoinT point,
1675                              Nlm_VoidPtr extra)
1676 {
1677     trace->start = trace->end = point;
1678 }
1679 
1680 
1681 static void OGL_Move_DragMA(MAPtr ma,
1682                             MA_TracePtr trace, Nlm_PoinT point,
1683                             Nlm_VoidPtr extra)
1684 {
1685     OGL_Move_DrawTrace(trace);
1686     trace->end = point;
1687     OGL_Move_DrawTrace(trace);
1688 }
1689 
1690 
1691 static void OGL_Move_ReleaseMA(MAPtr ma,
1692                                MA_TracePtr trace, Nlm_PoinT point,
1693                                Nlm_VoidPtr extra)
1694 {
1695     OGL_Move_DrawTrace(trace);
1696     trace->end = point;
1697 
1698     if (Nlm_EqualPt(trace->start, trace->end))
1699         return;
1700 
1701     {                           /* do the move transform */
1702         TOGL_Data *OGL_Data = MAToOGL(ma);
1703 
1704         OGL_Move3D(OGL_Data, (Int2) (trace->end.x - trace->start.x),
1705                    (Int2) (trace->end.y - trace->start.y));
1706         OGL_DrawViewer3D(OGL_Data);
1707 
1708     }
1709 
1710     trace->start = trace->end;
1711 }
1712 
1713 
1714 static void OLG_Move_CancelMA(MAPtr ma,
1715                               MA_TracePtr trace, Nlm_PoinT point,
1716                               Nlm_VoidPtr extra)
1717 {
1718     OGL_Move_DrawTrace(trace);
1719 }
1720 
1721 
1722 /*
1723  * ZOOM
1724  */
1725 
1726 static void OGL_Zoom_DrawTrace(MA_TracePtr trace)
1727 {
1728     Nlm_RecT rubber_box;
1729     if (Nlm_EqualPt(trace->start, trace->end))
1730         return;
1731 
1732 #ifdef WIN_MOTIF
1733     Nlm_SetColor(0xf1);
1734 #endif
1735     Nlm_InvertMode();
1736     Nlm_LoadRect(&rubber_box, trace->start.x, trace->start.y,
1737                  trace->end.x, trace->end.y);
1738     Nlm_FrameRect(&rubber_box); /* draw the frame */
1739     Nlm_CopyMode();
1740 }
1741 
1742 static void OGL_Zoom_PressMA(MAPtr ma,
1743                              MA_TracePtr trace, Nlm_PoinT point,
1744                              Nlm_VoidPtr extra)
1745 {
1746     trace->start = trace->end = point;
1747 }
1748 
1749 
1750 /* constrain the rubber band shape as it's dragged to match the aspect
1751    ratio of the OpenGL region */
1752 static void OGL_Zoom_DragMA(MAPtr ma,
1753                             MA_TracePtr trace, Nlm_PoinT point,
1754                             Nlm_VoidPtr extra)
1755 {
1756     TOGL_Data *OGL_Data = MAToOGL(ma);
1757     GLint viewport[4];
1758 
1759     glGetIntegerv(GL_VIEWPORT, viewport);
1760 
1761     OGL_Zoom_DrawTrace(trace);
1762     if (point.y >= trace->start.y)
1763         point.y = trace->start.y +
1764             abs(trace->end.x - trace->start.x) *
1765             viewport[3] / viewport[2];
1766     else
1767         point.y = trace->start.y -
1768             abs(trace->end.x - trace->start.x) *
1769             viewport[3] / viewport[2];
1770     trace->end = point;
1771     OGL_Zoom_DrawTrace(trace);
1772 }
1773 
1774 
1775 static void OGL_Zoom_ReleaseMA(MAPtr ma,
1776                                MA_TracePtr trace, Nlm_PoinT point,
1777                                Nlm_VoidPtr extra)
1778 {
1779     OGL_Zoom_DrawTrace(trace);
1780 
1781     if (Nlm_EqualPt(trace->start, trace->end))
1782         return;
1783 
1784     {                           /* do the zoom */
1785         TOGL_Data *OGL_Data = MAToOGL(ma);
1786 
1787         OGL_Zoom3D(OGL_Data, trace->start.x, trace->start.y,
1788                    trace->end.x, trace->end.y);
1789         OGL_DrawViewer3D(OGL_Data);
1790 
1791     }
1792 }
1793 
1794 static void OGL_Zoom_CancelMA(MAPtr ma,
1795                               MA_TracePtr trace, Nlm_PoinT point,
1796                               Nlm_VoidPtr extra)
1797 {
1798     OGL_Zoom_DrawTrace(trace);
1799 }
1800 
1801 
1802 /*
1803  * ROTATE
1804  */
1805 
1806 
1807 static void OGL_Rotate_PressMA(MAPtr ma,
1808                                MA_TracePtr trace, Nlm_PoinT point,
1809                                Nlm_VoidPtr extra)
1810 {
1811     trace->start = point;
1812 }
1813 
1814 
1815 
1816 static void OGL_Rotate_DragMA(MAPtr ma,
1817                               MA_TracePtr trace, Nlm_PoinT point,
1818                               Nlm_VoidPtr extra)
1819 {
1820     TOGL_Data *OGL_Data;
1821     GLint viewport[4];
1822 
1823     OGL_RotatePivots3DPtr pivot;
1824     if (Nlm_EqualPt(trace->start, point))
1825         return;
1826 
1827     OGL_Data = MAToOGL(ma);
1828     pivot = (OGL_RotatePivots3DPtr) extra;
1829 
1830     glGetIntegerv(GL_VIEWPORT, viewport);
1831 
1832     OGL_ViewerRotate((Nlm_SlatE) OGL_Data->Panel,
1833                      (Int4) ((180.0 * (point.x - trace->start.x)) /
1834                              viewport[2]), pivot->H, TRUE, FALSE);
1835 
1836     OGL_ViewerRotate((Nlm_SlatE) OGL_Data->Panel,
1837                      (Int4) ((180.0 * (trace->start.y - point.y)) /
1838                              viewport[3]), pivot->V, TRUE, TRUE);
1839     trace->start = point;
1840 }
1841 
1842 
1843 /*
1844  * RESET
1845  */
1846 
1847 static void OGL_ResetMA(MAPtr ma,
1848                         MA_TracePtr trace, Nlm_PoinT point,
1849                         Nlm_VoidPtr extra)
1850 {
1851     VERIFY(MA_UnsetAll(ma));
1852 }
1853 
1854 
1855 static Nlm_Boolean OGL_SetStdMouse(TOGL_Data * OGL_Data,
1856                                    Nlm_enumStdMAOGL action)
1857 {
1858     if (OGL_Data == NULL)
1859         return FALSE;
1860     return MA_SetGroup(OGL_Data->ma_std_group[action]);
1861 }
1862 
1863 
1864 
1865 /* Initialize MA for the viewer
1866 */
1867 
1868 static Nlm_Boolean OGL_InitializeMA(TOGL_Data * OGL_Data)
1869 {
1870     MAPtr ma = OGL_Data->ma;
1871 
1872     /* rotate */
1873     MActionPtr rotate_press =
1874         MA_AddAction(ma, MK_Normal, MA_Press, OGL_Rotate_PressMA, NULL,
1875                      NULL);
1876 
1877     static OGL_RotatePivots3D RotateDrag_YX = { ROTATE_Y, ROTATE_X };
1878     MActionPtr rotate_drag_YX =
1879         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1880                      &RotateDrag_YX, NULL);
1881     MA_GroupPtr rotate_group_YX = MA_AddGroup(ma, "Rotate_YX",
1882                                               rotate_press, MA_ONLY,
1883                                               rotate_drag_YX, MA_ONLY,
1884                                               NULL);
1885 
1886     static OGL_RotatePivots3D RotateDrag_ZX = { ROTATE_Z, ROTATE_X };
1887     MActionPtr rotate_drag_ZX =
1888         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1889                      &RotateDrag_ZX, NULL);
1890     MA_GroupPtr rotate_group_ZX = MA_AddGroup(ma, "Rotate_ZX",
1891                                               rotate_press, MA_ONLY,
1892                                               rotate_drag_ZX, MA_ONLY,
1893                                               NULL);
1894 
1895     static OGL_RotatePivots3D RotateDrag_YZ = { ROTATE_Y, ROTATE_Z };
1896     MActionPtr rotate_drag_YZ =
1897         MA_AddAction(ma, MK_Normal, MA_Drag, OGL_Rotate_DragMA,
1898                      &RotateDrag_YZ, NULL);
1899     MA_GroupPtr rotate_group_YZ = MA_AddGroup(ma, "Rotate_YZ",
1900                                               rotate_press, MA_ONLY,
1901                                               rotate_drag_YZ, MA_ONLY,
1902                                               NULL);
1903 
1904     /* move */
1905     MActionPtr move_press =
1906         MA_AddAction(ma, MK_Shift, MA_Press, OGL_Move_PressMA, NULL, NULL);
1907     MActionPtr move_drag =
1908         MA_AddAction(ma, MK_Shift, MA_Drag, OGL_Move_DragMA, NULL, NULL);
1909     MActionPtr move_release =
1910         MA_AddAction(ma, MK_Shift, MA_Release, OGL_Move_ReleaseMA, NULL,
1911                      NULL);
1912     MActionPtr move_cancel =
1913         MA_AddAction(ma, MK_Shift, MA_Cancel, OLG_Move_CancelMA, NULL,
1914                      NULL);
1915 
1916     MA_GroupPtr move_group = MA_AddGroup(ma, "Move",
1917                                          move_press, MA_ONLY,
1918                                          move_drag, MA_ONLY,
1919                                          move_release, MA_ONLY,
1920                                          move_cancel, MA_ONLY,
1921                                          NULL);
1922 
1923     /* zoom */
1924     MActionPtr zoom_press =
1925         MA_AddAction(ma, MK_Ctrl, MA_Press, OGL_Zoom_PressMA, NULL, NULL);
1926     MActionPtr zoom_drag =
1927         MA_AddAction(ma, MK_Ctrl, MA_Drag, OGL_Zoom_DragMA, NULL, NULL);
1928     MActionPtr zoom_release =
1929         MA_AddAction(ma, MK_Ctrl, MA_Release, OGL_Zoom_ReleaseMA, NULL,
1930                      NULL);
1931     MActionPtr zoom_cancel =
1932         MA_AddAction(ma, MK_Ctrl, MA_Cancel, OGL_Zoom_CancelMA, NULL,
1933                      NULL);
1934 
1935     MA_GroupPtr zoom_group = MA_AddGroup(ma, "Zoom",
1936                                          zoom_press, MA_ONLY,
1937                                          zoom_drag, MA_ONLY,
1938                                          zoom_release, MA_ONLY,
1939                                          zoom_cancel, MA_ONLY,
1940                                          NULL);
1941 
1942     /* miscellaneous actions */
1943 /*
1944  this is done in the main program.  move it here after deleting viewer3d
1945     MActionPtr bg_hl_dclick =
1946         MA_AddAction(ma, MK_Normal, MA_DClick,  NULL, NULL,
1947         "Highlight-Prim or Background");
1948 */
1949 
1950     /* this group disables all mouse actions when set */
1951     MActionPtr reset_init =
1952         MA_AddAction(ma, MK_Normal, MA_Init, OGL_ResetMA, NULL, NULL);
1953 
1954     MA_GroupPtr reset_group = MA_AddGroup(ma, "No Action",
1955                                           reset_init, MA_SHARED,
1956                                           NULL);
1957 
1958     if (OGL_Data == NULL)
1959         return FALSE;
1960 
1961     { {                         /* "No-Action"s */
1962             int i, j;
1963             for (i = 0; i < MK_Default; i++)
1964                 for (j = 0; j < MA_Init; j++) {
1965                     VERIFY(MA_AddAction(ma, (enumMKey) i, (enumMAction) j,
1966                                         DoNothingMA, NULL, "No Action"));
1967                 }
1968     }
1969     }
1970 
1971     /* register the set of standard 3D-viewer groups */
1972     OGL_Data->ma_std_group[MouseOGL_DoNothing] = reset_group;
1973     OGL_Data->ma_std_group[MouseOGL_RotateYX] = rotate_group_YX;
1974     OGL_Data->ma_std_group[MouseOGL_RotateZX] = rotate_group_ZX;
1975     OGL_Data->ma_std_group[MouseOGL_RotateYZ] = rotate_group_YZ;
1976     OGL_Data->ma_std_group[MouseOGL_Move] = move_group;
1977     OGL_Data->ma_std_group[MouseOGL_Zoom] = zoom_group;
1978 
1979     /* Test, Setup defaults and Link viewer panel to MA */
1980     if (!rotate_press ||
1981         !rotate_drag_YX || !rotate_group_YX ||
1982         !rotate_drag_ZX || !rotate_group_ZX ||
1983         !rotate_drag_YZ || !rotate_group_YZ ||
1984         !move_press || !move_drag || !move_release ||
1985         !move_cancel || !move_group ||
1986         !zoom_press || !zoom_drag || !zoom_release ||
1987         !zoom_cancel || !zoom_group ||
1988 /*        !bg_hl_dclick    ||*/
1989         !reset_group ||
1990         !OGL_SetStdMouse(OGL_Data, MouseOGL_RotateYX) ||
1991         !OGL_SetStdMouse(OGL_Data, MouseOGL_Move) ||
1992         !OGL_SetStdMouse(OGL_Data, MouseOGL_Zoom) ||
1993 /*        !MA_SetAction(bg_hl_dclick, FALSE)  ||*/
1994         !MA_LinkPanel(ma, OGL_Data->Panel)) {
1995         MA_Reset(ma);
1996         return FALSE;
1997     }
1998 
1999     return TRUE;
2000 }
2001 
2002 
2003 /*
2004  *  Doing selection in OpenGL
2005  */
2006 
2007 void OGL_Select(TOGL_Data * OGL_Data, Nlm_Boolean SelectMode)
2008 {
2009     if (OGL_Data == NULL)
2010         return;
2011     OGL_Data->SelectMode = SelectMode;
2012     return;
2013 }
2014 
2015 
2016 void OGL_LoadName(Nlm_VoidPtr PtrValue)
2017 /* load a pointer onto the name stack.  compensate for possible long long */
2018 {
2019     Nlm_Int4 i;
2020 
2021     for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2022         glPopName();
2023 
2024     for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2025         glPushName((GLuint) (((long) PtrValue) >> (i * sizeof(GLuint) * 8))); /* 64 bits? */
2026 
2027     OGL_CurrentName = PtrValue;
2028 }
2029 
2030 
2031 Nlm_VoidPtr OGL_Hit(TOGL_Data * OGL_Data)
2032 /* this function looks through the hit stack and extracts the nearest hit */
2033 {
2034     GLuint *Hits, nNames, hit, p = 0, j, ZMin;
2035     long ZMinName = 0;          /* this is a hack, but should work for 64 bits */
2036 
2037     if (OGL_Data == NULL || OGL_Data->SelectBuffer == NULL)
2038         return NULL;
2039 
2040     Hits = (GLuint *) OGL_Data->SelectBuffer;
2041     for (hit=0; hit < OGL_Data->SelectHits; hit++) { /* loop over all hits */
2042         nNames = Hits[p];
2043         p++; /* move to zmin */
2044         /* look for *minimum* depth - that's the top object on the screen */
2045         if (hit == 0 || Hits[p] < ZMin) {
2046             ZMin = Hits[p];
2047             p += 2; /* skip over zmax to name stack */
2048             ZMinName = 0;
2049             /* currently only looks at the top of the name stack */
2050             for (j = 0; j < sizeof(Nlm_VoidPtr) / sizeof(GLuint); j++) {
2051                 ZMinName = ZMinName << (sizeof(GLuint) * 8);
2052                 ZMinName |= Hits[p + j];
2053             }
2054             p += nNames * sizeof(Nlm_VoidPtr) / sizeof(GLuint);
2055         } else
2056             p += nNames * sizeof(Nlm_VoidPtr) / sizeof(GLuint) + 2;
2057     }
2058     return (Nlm_VoidPtr) ZMinName;
2059 }
2060 
2061 
2062 void OGL_SetSelectPoint(TOGL_Data * OGL_Data, Nlm_PoinT Point)
2063 {
2064     Nlm_RecT rect;
2065 
2066     if (OGL_Data == NULL)
2067         return;
2068 
2069     OGL_Data->SelectPoint.x = Point.x;
2070     OGL_Data->SelectPoint.y = Point.y;
2071 
2072     /* translate window coords as passed to this function into coords
2073        relative to OpenGL area (the Panel) coords */
2074     Nlm_GetRect((Nlm_GraphiC)OGL_Data->Panel, &rect);
2075     OGL_Data->SelectPoint.x -= rect.left;
2076     OGL_Data->SelectPoint.y -= rect.top;
2077 
2078 #ifdef WIN_MSWIN
2079     /* For some reason, windows requires a fudge to get the mouse pointer
2080        to be more accurate */
2081     OGL_Data->SelectPoint.x += 1;
2082     OGL_Data->SelectPoint.y -= 4;
2083 #endif
2084 }
2085 
2086 
2087 /*
2088  *  functions used to create, do, and finish drawing
2089  */
2090 
2091 
2092 static void OGL_DeleteViewer3D(TOGL_Data * OGL_Data)
2093 /* delete OGL_Data */
2094 {
2095     if (OGL_Data == NULL)
2096         return;
2097 
2098     MA_Destroy(OGL_Data->ma);
2099 
2100     /* to do: someone else is getting rid of Panel
2101        if ( OGL_Data->Panel )
2102        Nlm_Remove( OGL_Data->Panel );
2103      */
2104 
2105     /* delete the display lists */
2106 #ifndef MESA
2107     /* There's some bug (?) in Mesa that causes a crash here, maybe because
2108        we're trying to delete non-existent lists? Dunno... */
2109     glDeleteLists(OGL_Data->Layers->FirstLayer - 1, OGLMAXLAYERS);
2110 #endif
2111 
2112     /* free items on the heap */
2113     MemFree(OGL_Data->Layers);
2114     MemFree(OGL_Data->ModelMatrix);
2115     MemFree(OGL_Data);
2116 }
2117 
2118 
2119 static void OGL_ResetViewerProc_CB(Nlm_PaneL panel)
2120 {
2121     TOGL_Data *OGL_Data = PanelToOGL(panel);
2122     if (!OGL_Data)
2123         return;
2124 
2125     OGL_Data = NULL;
2126     OGL_DeleteViewer3D(PanelToOGL(panel));
2127 }
2128 
2129 
2130 typedef struct {
2131 #ifdef WIN_MAC
2132     int familyID;
2133 #else
2134     char *name;
2135 #endif
2136 } OGLFontListItem;
2137 
2138 #define MAXFONTS 1000
2139 static OGLFontListItem OGLFontList[MAXFONTS]; /* should really be dynamic... */
2140 static int nFonts = 0;
2141 
2142 #if defined(WIN32)
2143 int CALLBACK Nlm_FindFontCB(
2144   ENUMLOGFONTEX *lpelfe,
2145   NEWTEXTMETRICEX *lpntme,
2146   DWORD FontType,
2147   LPARAM lParam )
2148 {
2149     OGLFontList[nFonts].name = strdup(lpelfe->elfLogFont.lfFaceName);
2150     Nlm_PopupItem((Nlm_PopuP)lParam, OGLFontList[nFonts].name);
2151     nFonts++;
2152     if (nFonts == MAXFONTS)
2153         return 0;
2154     else
2155         return 1;
2156 }
2157 
2158 #elif defined(WIN_MOTIF)
2159 static char *standardXFont = "9x15";
2160 #endif
2161 
2162 /* called during app initialization, to set up font family menu */
2163 NLM_EXTERN void Nlm_FindAvailableFonts(Nlm_PopuP pupmenu)
2164 {
2165 #if defined(WIN32)
2166     LOGFONT lf;
2167     HDC hdc = wglGetCurrentDC();
2168     /* to enumerate all styles of all fonts for the ANSI character set */
2169     lf.lfFaceName[0] = '\0';
2170     lf.lfCharSet = ANSI_CHARSET;
2171     lf.lfPitchAndFamily = 0;
2172     EnumFontFamiliesEx((HDC)hdc, (LPLOGFONT)&lf, (FONTENUMPROC)Nlm_FindFontCB,
2173                         (LPARAM)pupmenu, (DWORD)0);
2174 
2175 #elif defined(WIN_MAC)
2176         int i;
2177         char name[255];
2178 
2179 #define ADD_FONT(str,ID) do { \
2180                            OGLFontList[nFonts].familyID = (ID); \
2181                            Nlm_PopupItem(pupmenu, (str)); \
2182                            nFonts++; \
2183                            if (nFonts == MAXFONTS) return; \
2184                          } while (0)
2185 
2186         /* There's gotta be a better way to get a list of
2187            available fonts... this takes forever even on a fast mac! */
2188         for (i=2; i<=16383; i++) {
2189                 if (i==256) i=1024;
2190                 if (!RealFont(i,12)) continue;
2191                 memset(name, '\0', 255);
2192                 GetFontName(i, (unsigned char *)name);
2193                 if (name[0] != '\0') {
2194                   ADD_FONT(name+1, i); /* for some reason, name starts at 2nd character? */
2195                 }
2196         }
2197 
2198 #elif defined(WIN_MOTIF)
2199     extern Display *Nlm_currentXDisplay;
2200     char **list, fnd_fam[256], *p;
2201     int i, j, tot;
2202     XFontStruct *xfs;
2203 
2204 #define ADD_FONT(str) do { \
2205                         OGLFontList[nFonts].name = strdup((str)); \
2206                         Nlm_PopupItem(pupmenu, (str)); \
2207                         nFonts++; \
2208                         if (nFonts == MAXFONTS) return; \
2209                       } while (0);
2210 
2211     xfs = XLoadQueryFont(Nlm_currentXDisplay, standardXFont);
2212     if (xfs) {
2213         ADD_FONT(standardXFont);
2214         XFreeFont(Nlm_currentXDisplay, xfs);
2215     }
2216     list = XListFonts(Nlm_currentXDisplay,
2217                       "-*-*-*-*-*-*-*-*-*-*-*-*-*-*",
2218                       100000, &tot);
2219     for (i=0; i<tot; i++) {
2220         strncpy(fnd_fam,list[i]+1,256);
2221         p = strchr(fnd_fam,'-');
2222         if (!p) continue;
2223         p = strchr(p+1,'-');
2224         if (!p) continue;
2225         *p = '\0';
2226         /* use foundry-family pair (i.e., adobe-courier)
2227            as "name" of font to store */
2228         for (j=0; j<nFonts; j++) {
2229             if (strcmp(OGLFontList[j].name,fnd_fam) == 0) break;
2230         }
2231         if (j == nFonts) ADD_FONT(fnd_fam);
2232     }
2233     XFreeFontNames(list);
2234 #endif
2235 }
2236 
2237 NLM_EXTERN void SetOGLFont(TOGL_Data *OGL_Data, Nlm_Int2 fontNameIndex, Nlm_Int2 fontSize,
2238     Nlm_Boolean isBold, Nlm_Boolean isItalic, Nlm_Boolean isUnderlined)
2239 {
2240     static Nlm_Int2 currentIndex = -1, currentSize = -1;
2241     static Nlm_Boolean currentBold = FALSE, currentItalic = FALSE,
2242                        currentUnderlined = FALSE;
2243 
2244     fontNameIndex--; /* vibrant menus start at one */
2245     if (fontNameIndex == currentIndex && currentSize == fontSize &&
2246         isBold == currentBold && isItalic == currentItalic &&
2247         isUnderlined == currentUnderlined)
2248         return; /* only switch fonts if necessary */
2249     currentIndex = fontNameIndex;
2250     currentSize = fontSize;
2251     currentBold = isBold;
2252     currentItalic = isItalic;
2253     currentUnderlined = isUnderlined;
2254 
2255 #if defined(WIN32)
2256     {
2257         HDC hdc;
2258         HGDIOBJ currentFont;
2259         static HGDIOBJ newFont = NULL;
2260         SIZE TextSize;
2261 
2262         /* delete font used in previous call */
2263         if (newFont) DeleteObject(newFont);
2264 
2265         hdc = wglGetCurrentDC();
2266         newFont =
2267             CreateFont(
2268                 -MulDiv(currentSize, GetDeviceCaps(hdc, LOGPIXELSY), 72),
2269                 0, 0, 0, currentBold ? FW_BOLD : FW_NORMAL,
2270                 currentItalic ? TRUE : FALSE,
2271                 currentUnderlined ? TRUE : FALSE, FALSE, ANSI_CHARSET,
2272                 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
2273                 FF_DONTCARE, (LPCTSTR) OGLFontList[currentIndex].name
2274             );
2275         currentFont = SelectObject(hdc, newFont);
2276         wglUseFontBitmaps(hdc, 0, 255, OGLFONTBASE);
2277 
2278         /* Get the size of an average character */
2279         GetTextExtentPoint32(hdc, "A", 1, &TextSize);
2280         OGL_Data->SpaceWidth = TextSize.cx;
2281         OGL_Data->SpaceHeight = TextSize.cy;
2282 
2283         /* restore previous font */
2284         SelectObject(hdc, currentFont);
2285     }
2286 #elif defined(WIN_MAC)
2287         {
2288         FontInfo fInfo;
2289         GrafPtr currentPort;
2290         GLint ID = OGLFontList[currentIndex].familyID;
2291         Style face = 0;
2292 
2293                 if (!RealFont(ID, currentSize))
2294                         return;
2295 
2296                 if (isBold) face |= bold;
2297                 if (isItalic) face |= italic;
2298                 if (isUnderlined) face |= underline;
2299 
2300         aglUseFont(aglGetCurrentContext(),
2301                    ID, face, currentSize,
2302                    0, 256, OGLFONTBASE);
2303 
2304         GetPort(&currentPort); /* store the current port's font info */
2305         TextFont(ID);
2306         TextFace(face);
2307         TextSize(currentSize);
2308         OGL_Data->SpaceWidth = CharWidth('A');
2309         GetFontInfo(&fInfo);
2310         OGL_Data->SpaceHeight = fInfo.ascent + fInfo.descent;
2311         TextFont(GetPortTextFont(currentPort)); /* restore previous font */
2312         TextFace(GetPortTextFace(currentPort));
2313         TextSize(GetPortTextSize(currentPort));
2314     }
2315 
2316 #elif defined(WIN_MOTIF)
2317     {
2318         static XFontStruct *fontInfo = NULL;
2319         extern Display *Nlm_currentXDisplay;
2320         char query[256], **list, *pos;
2321         int nFound, ii, ib, size, diff, mindiff, closest;
2322         static const char
2323             *bold[] = { "bold", "black", NULL },
2324             *unbold[] = { "medium", "regular", NULL }, **boldSel,
2325             *ital[] = { "i", "o", NULL },
2326             *unital[] = { "r", NULL }, **italSel;
2327 
2328         if (fontInfo) XFreeFont(Nlm_currentXDisplay, fontInfo);
2329 
2330         if (strcmp(OGLFontList[currentIndex].name, standardXFont) == 0) {
2331             fontInfo = XLoadQueryFont(Nlm_currentXDisplay, standardXFont);
2332         } else {
2333 
2334             /* first find fonts with right typeface; bold, italic if possible */
2335             boldSel = currentBold ? bold : unbold;
2336             italSel = currentItalic ? ital : unital;
2337             for (ib = 0; boldSel[ib] ; ib++) {
2338                 for (ii = 0; italSel[ii] ; ii++) {
2339                     sprintf(query,"-%s-%s-%s-*-*-*-*-*-*-*-*-*-*",
2340                             OGLFontList[currentIndex].name,
2341                             boldSel[ib], italSel[ii]);
2342                     list = XListFonts(Nlm_currentXDisplay, query, 1000, &nFound);
2343                     if (nFound) break;
2344                 }
2345                 if (nFound) break;
2346             }
2347             if (!nFound) { /* can't find any of this style; use any available */
2348                 sprintf(query,"-%s-*-*-*-*-*-*-*-*-*-*-*-*",
2349                         OGLFontList[currentIndex].name);
2350                 list = XListFonts(Nlm_currentXDisplay, query, 1000, &nFound);
2351             }
2352             /* now find closest pixelsize to that requested */
2353             for (ii = 0; ii < nFound; ii++) {
2354                 /* find start of pixelsize field of font string */
2355                 pos = list[ii];
2356                 for (ib = 0; ib < 6; ib++) pos = strchr(pos+1, '-');
2357                 pos++;
2358                 /* store closest size match to requested */
2359                 size = atoi(pos);
2360                 /* use 2 * menu size setting as pixel size */
2361                 diff = abs(size - currentSize*2);
2362                 if (ii == 0 || diff < mindiff) {
2363                     mindiff = diff;
2364                     closest = ii;
2365                 }
2366             }
2367             fontInfo = XLoadQueryFont(Nlm_currentXDisplay, list[closest]);
2368             XFreeFontNames(list);
2369         }
2370 
2371         if (fontInfo->per_char == NULL) {
2372             OGL_Data->SpaceWidth = fontInfo->max_bounds.width;
2373             OGL_Data->SpaceHeight = fontInfo->max_bounds.ascent +
2374                                     fontInfo->max_bounds.descent;
2375         } else {
2376             XCharStruct *charA = &(fontInfo->per_char['A']);
2377             OGL_Data->SpaceWidth = charA->width;
2378             OGL_Data->SpaceHeight = charA->ascent + charA->descent;
2379         }
2380 
2381         glXUseXFont(fontInfo->fid,
2382                     fontInfo->min_char_or_byte2,
2383                     fontInfo->max_char_or_byte2 -
2384                       fontInfo->min_char_or_byte2 + 1,
2385                     OGLFONTBASE + fontInfo->min_char_or_byte2);
2386     }
2387 #endif
2388 }
2389 
2390 
2391 TOGL_Data *OGL_CreateViewer(Nlm_GrouP prnt,
2392                             Uint2Ptr width, Uint2 height,
2393                             Int4 flags,
2394                             Nlm_MenU ma_group_menu,
2395                             Nlm_MenU ma_action_menu,
2396                             Nlm_MAInitOGLFunc ma_init_func,
2397                             VoidPtr ma_init_data)
2398                              /* initialize the OpenGL library */
2399 {
2400     TOGL_Data *OGL_Data;
2401     Nlm_Uint2 x_width;
2402 
2403     OGL_Data = (TOGL_Data *) MemNew(sizeof(TOGL_Data));
2404     if (OGL_Data == NULL)
2405         return NULL;
2406 
2407     OGL_Data->ModelMatrix = (Nlm_VoidPtr) MemNew(16 * sizeof(GLdouble));
2408     if (OGL_Data->ModelMatrix == NULL)
2409         return NULL;
2410     OGL_Data->NeedCameraSetup = FALSE;
2411 
2412     OGL_Data->Layers = (TOGL_Layers *) MemNew(sizeof(TOGL_Layers));
2413     if (OGL_Data->Layers == NULL)
2414         return NULL;
2415 
2416     OGL_Data->Layers->SelectedLayer = 0;
2417     OGL_SetLayers(OGL_Data, FALSE); /* null all the layers out */
2418 
2419     OGL_Data->IsPlaying = FALSE; /* animation off */
2420     OGL_Data->Tick = 0.01;
2421     OGL_Data->ParentWindow = Nlm_ParentWindow((Nlm_Handle) prnt);
2422     OGL_Data->PaletteExpanded = NULL;
2423     OGL_Data->PaletteIndex = NULL;
2424 
2425     OGL_Data->SelectMode = FALSE;
2426     OGL_Data->SelectBuffer =
2427         (Nlm_VoidPtr) MemNew(OGLSELECTBUFFER * sizeof(GLuint));
2428     if (OGL_Data->SelectBuffer == NULL)
2429         return NULL;
2430 
2431     OGL_Data->Panel = Nlm_Autonomous3DPanel(prnt,
2432                                             (Int2) * width, (Int2) height,
2433                                             OGL_DrawViewer3D_CB,
2434                                             ((flags & Y_ROTATE_SBAR) ?
2435                                              OGL_ViewerVScrollProc : NULL),
2436                                             ((flags & X_ROTATE_SBAR) ?
2437                                              OGL_ViewerHScrollProc : NULL),
2438                                             sizeof(MAPtr),
2439                                             OGL_ResetViewerProc_CB, NULL,
2440                                             &OGL_Data->IndexMode, &OGL_Data->display,
2441                                             &OGL_Data->visinfo);
2442 
2443 
2444     if (flags & Z_ROTATE_SBAR) {
2445         OGL_Data->Z_rotate =
2446             Nlm_ScrollBar(prnt, 1, 0, OGL_ViewerZScrollProc);
2447         if (!OGL_Data->Z_rotate) {
2448             MemFree(OGL_Data);
2449             if (OGL_Data->Panel)
2450                 Nlm_Remove(OGL_Data->Panel);
2451             return NULL;
2452         }
2453         Nlm_SetObjectExtra(OGL_Data->Z_rotate, OGL_Data->Panel, NULL);
2454     }
2455      {
2456         Nlm_RecT rect;
2457         Nlm_GetPosition(OGL_Data->Panel, &rect);
2458         rect.right = (Int2) (rect.left + *width);
2459         rect.bottom = (Int2) (rect.top + height);
2460         OGL_SetPosition3D(OGL_Data, &rect);
2461         x_width = (Uint2) (rect.right - rect.left);
2462     }
2463 
2464     if (flags & X_ROTATE_SBAR) {
2465         Nlm_BaR sb = Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel);
2466         Nlm_CorrectBarValue(sb, 0);
2467         Nlm_SetRange(sb, 10, 10, 360);
2468     }
2469     if (flags & Y_ROTATE_SBAR) {
2470         Nlm_BaR sb = Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel);
2471         Nlm_CorrectBarValue(sb, 0);
2472         Nlm_SetRange(sb, 10, 10, 360);
2473     }
2474     if (flags & Z_ROTATE_SBAR) {
2475         Nlm_SetRange(OGL_Data->Z_rotate, 10, 10, 360);
2476         Nlm_CorrectBarValue(OGL_Data->Z_rotate, 180);
2477     }
2478 
2479     OGL_Data->ma = MA_Create(ma_group_menu, ma_action_menu);
2480     MA_SetExtra(OGL_Data->ma, OGL_Data);
2481 
2482     if (!OGL_InitializeMA(OGL_Data)) {
2483         MemFree(OGL_Data);
2484         if (OGL_Data->Z_rotate)
2485             Nlm_Remove(OGL_Data->Z_rotate);
2486         if (OGL_Data->Panel)
2487             Nlm_Remove(OGL_Data->Panel);
2488         return NULL;
2489     }
2490 
2491     if (ma_init_func && !(*ma_init_func) (OGL_Data->ma, ma_init_data)) {
2492         MemFree(OGL_Data);
2493         if (OGL_Data->Z_rotate)
2494             Nlm_Remove(OGL_Data->Z_rotate);
2495         if (OGL_Data->Panel)
2496             Nlm_Remove(OGL_Data->Panel);
2497         return NULL;
2498     }
2499 
2500     *width = x_width;
2501 
2502     if (!OGL_qobj) {
2503         /* allocate quadric object (once only) */
2504         OGL_qobj = gluNewQuadric();
2505         if (!OGL_qobj) return NULL;
2506         gluQuadricDrawStyle(OGL_qobj, GLU_FILL);
2507         gluQuadricNormals(OGL_qobj, GLU_SMOOTH);
2508         gluQuadricOrientation(OGL_qobj, GLU_OUTSIDE);
2509     }
2510 
2511     return OGL_Data;
2512 }
2513 
2514 void OGL_InitializeLists(TOGL_Data * OGL_Data)
2515 {
2516     OGL_Data->Layers->FirstLayer = glGenLists((GLsizei) OGLMAXLAYERS);
2517     OGL_Data->Layers->FirstLayer++; /* avoid weird bug in windows OpenGL */
2518     OGL_Data->Layers->LastLayer = OGL_Data->Layers->FirstLayer;
2519 }
2520 
2521 #ifdef WIN_MAC
2522 static Nlm_Boolean drawingOffscreen = FALSE;
2523 #endif
2524 
2525 void OGL_DrawViewer3D(TOGL_Data * OGL_Data)
2526 /* does the drawing */
2527 {
2528     GLint Viewport[4];
2529     static GLint prevW = -1, prevH = -1;
2530     GLfloat LightPosition[4], aspect;
2531     Nlm_Int4 TotalColors;
2532     Nlm_Uint4 iList;
2533     int i;
2534 
2535 #ifdef DEBUG_GL
2536     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error entering DrawViewer3D");
2537 #endif
2538 
2539     if (OGL_Data == NULL) return;
2540 
2541 #ifdef WIN_MOTIF
2542     /* need to know if in color index mode - i.e., after context established */
2543     if (!glXGetCurrentContext()) return;
2544 #endif
2545 
2546 #ifdef WIN_MAC
2547         if (drawingOffscreen) {
2548         aglDisable(aglGetCurrentContext(), AGL_BUFFER_RECT);
2549     } else {
2550         /* HACK to limit opengl to correct region of the (Cn3D) window */
2551         GLint wrect[4];
2552         Nlm_RecT r;
2553         AGLContext ctx = aglGetCurrentContext();
2554         Nlm_GetRect((Nlm_GraphiC) OGL_Data->Panel, &r);
2555         wrect[0] = r.left;
2556         wrect[1] = r.top - Nlm_hScrollBarHeight  - 4; /* this last column is simply tweaks */
2557         wrect[2] = r.right - r.left + 1          - 2; /* to make window position look nice */
2558         wrect[3] = r.bottom - r.top + 1          - 3;
2559         aglSetInteger(ctx, AGL_BUFFER_RECT, wrect);
2560         aglEnable(ctx, AGL_BUFFER_RECT);
2561     }
2562 #endif
2563 
2564     /* set background color */
2565     if (OGL_Data->IndexMode) {
2566         TotalColors = ValNodeLen(OGL_Data->PaletteExpanded);
2567 #ifdef WIN32
2568         glClearIndex((GLfloat) (TotalColors + 10));
2569 #else
2570         glClearIndex((GLfloat) TotalColors);
2571 #endif
2572 
2573     } else {
2574         glClearColor(1.0*OGL_Data->Background.rgb[0]/255, 
2575                      1.0*OGL_Data->Background.rgb[1]/255, 
2576                      1.0*OGL_Data->Background.rgb[2]/255, 1.0);
2577     }
2578 
2579     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2580 
2581     glGetIntegerv(GL_VIEWPORT, Viewport);
2582 
2583     /* only do this (expensive) stuff if we have to */
2584     if (Viewport[2] != prevW || Viewport[3] != prevH ||
2585         OGL_Data->SelectMode || OGL_Data->NeedCameraSetup) {
2586 
2587         prevW = Viewport[2];
2588         prevH = Viewport[3];
2589         if (OGL_Data->SelectMode)
2590             OGL_Data->NeedCameraSetup = TRUE; /* will need to redo camera next time */
2591         else
2592             OGL_Data->NeedCameraSetup = FALSE;
2593 
2594         glMatrixMode(GL_PROJECTION);
2595         glLoadIdentity();
2596 
2597         if (OGL_Data->SelectMode) {
2598             glSelectBuffer((GLsizei) OGLSELECTBUFFER,
2599                            (GLuint *) OGL_Data->SelectBuffer);
2600             glRenderMode(GL_SELECT);
2601             glInitNames();
2602             for (i = 0; i < sizeof(Nlm_VoidPtr) / sizeof(GLuint); i++)
2603                 glPushName(0);
2604             gluPickMatrix((GLdouble) (OGL_Data->SelectPoint.x),
2605                           (GLdouble) (Viewport[3] - OGL_Data->SelectPoint.y),
2606                           1.0, 1.0, Viewport);
2607         }
2608 
2609         aspect = ((GLfloat)(Viewport[2])) / Viewport[3];
2610         gluPerspective(OGL_Data->CameraAngle * 180.0/acos(-1), /* viewing angle (degrees) */
2611                        aspect,                                 /* w/h aspect    */
2612                        0.5 * OGL_Data->CameraDistance,         /* near clipping plane */
2613                        1.5 * OGL_Data->CameraDistance);        /* far clipping plane  */
2614         gluLookAt(0.0,0.0,OGL_Data->CameraDistance,  /* the camera position */
2615                   OGL_Data->CameraDirection[0],      /* the "look-at" point */
2616                   OGL_Data->CameraDirection[1],
2617                   0.0,
2618                   0.0,1.0,0.0);                      /* the up direction */
2619 
2620         glMatrixMode(GL_MODELVIEW);
2621         glLoadIdentity();
2622 
2623         /* set up the lighting */
2624         LightPosition[0] = 0.0; /* same as eye */
2625         LightPosition[1] = 0.0;
2626         LightPosition[2] = OGL_Data->CameraDistance;
2627         LightPosition[3] = 0.0;  /* directional light (faster) when 0.0 */
2628         glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
2629 
2630 
2631         if (OGL_Data->IndexMode == FALSE) {
2632             glLightfv(GL_LIGHT0, GL_AMBIENT, Color_Off);
2633             glLightfv(GL_LIGHT0, GL_DIFFUSE, Color_MostlyOn);
2634             glLightfv(GL_LIGHT0, GL_SPECULAR, Color_Off);
2635 
2636             /* clear these material colors */
2637             glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Color_Off);
2638             glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Color_Off);
2639 
2640         } else { /* color index mode */
2641             glLightfv(GL_LIGHT0, GL_AMBIENT, Color_Off);
2642             glLightfv(GL_LIGHT0, GL_DIFFUSE, Color_On);
2643             glLightfv(GL_LIGHT0, GL_SPECULAR, Color_Off);
2644         }
2645 
2646         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, Color_On); /* global ambience */
2647         glEnable(GL_LIGHTING);
2648         glEnable(GL_LIGHT0);
2649 
2650         glShadeModel(GL_SMOOTH);
2651         glEnable(GL_DEPTH_TEST);
2652 
2653         /* turn on culling to speed rendering */
2654         glEnable(GL_CULL_FACE);
2655         glCullFace(GL_BACK);
2656         glFrontFace(GL_CCW);
2657     }
2658 
2659     glLoadIdentity();
2660     glMultMatrixd((GLdouble *) OGL_Data->ModelMatrix);
2661 
2662     /* selectively display the layers */
2663     if (OGL_Data->Layers->SelectedLayer) {
2664         glCallList(OGL_Data->Layers->SelectedLayer);
2665     } else {
2666         for (iList = OGL_Data->Layers->FirstLayer;
2667              iList <= OGL_Data->Layers->LastLayer; iList++) {
2668             if (OGL_GetLayer(OGL_Data, iList - OGL_Data->Layers->FirstLayer)) {
2669                 glCallList(iList);
2670             }
2671         }
2672     }
2673 
2674     /* display transparent spheres */
2675     if (!OGL_Data->IndexMode) {
2676         OGL_SetColor(OGL_Data, NULL, GL_NONE, 1.0); /* clear all color states */
2677         OGL_RenderTransparentSpheres(OGL_Data);
2678     }
2679 
2680     OGL_CheckForErrors(); /* check GL error status */
2681 
2682     glFlush();
2683 
2684     if (OGL_Data->SelectMode)
2685         OGL_Data->SelectHits = glRenderMode(GL_RENDER);
2686 
2687     else { /* regular rendering mode */
2688         /* swap when double buffering */
2689 #if defined(WIN32)
2690         wglSwapLayerBuffers(wglGetCurrentDC(), WGL_SWAP_MAIN_PLANE);/**/
2691         /*SwapBuffers(wglGetCurrentDC());*/
2692 #elif defined(WIN_MOTIF)
2693         glXSwapBuffers((Display *) OGL_Data->display, glXGetCurrentDrawable());
2694 #elif defined(WIN_MAC)
2695         aglSwapBuffers(aglGetCurrentContext());
2696 #endif
2697 
2698     }
2699 
2700 #ifdef DEBUG_GL
2701     if (OGL_CheckForErrors()) Message(MSG_POST, "GL error leaving DrawViewer3D");
2702 #endif
2703 }
2704 
2705 
2706 void OGL_Reset(TOGL_Data * OGL_Data)
2707 /* reset the transform matrix */
2708 {
2709     Nlm_FloatLo diff;
2710 
2711     if (OGL_Data == NULL)
2712         return;
2713 
2714     OGL_Data->MaxSize = (Nlm_FloatLo)
2715         fabs(OGL_Data->BoundBox.x[0] - OGL_Data->BoundBox.x[1]);
2716     diff = fabs(OGL_Data->BoundBox.y[0] - OGL_Data->BoundBox.y[1]);
2717     if (diff > OGL_Data->MaxSize)
2718         OGL_Data->MaxSize = diff;
2719     diff = fabs(OGL_Data->BoundBox.z[0] - OGL_Data->BoundBox.z[1]);
2720     if (diff > OGL_Data->MaxSize)
2721         OGL_Data->MaxSize = diff;
2722 
2723     /* set up the initial view point */
2724     OGL_Data->CameraDistance = (GLfloat) 5.0 *OGL_Data->MaxSize;
2725     OGL_Data->CameraDirection[0] = 0.0;
2726     OGL_Data->CameraDirection[1] = 0.0;
2727     OGL_Data->CameraAngle = 2.0 *
2728         atan((fabs(OGL_Data->BoundBox.y[0] - OGL_Data->BoundBox.y[1]) / 2.0) /
2729               OGL_Data->CameraDistance);
2730     OGL_Data->NeedCameraSetup = TRUE;
2731 
2732     /* translate model so origin is at bounding box center */
2733     glMatrixMode(GL_MODELVIEW);
2734     glLoadIdentity();
2735     glTranslatef(
2736         (GLfloat) -((OGL_Data->BoundBox.x[0] + OGL_Data->BoundBox.x[1]) / 2.0),
2737         (GLfloat) -((OGL_Data->BoundBox.y[0] + OGL_Data->BoundBox.y[1]) / 2.0),
2738         (GLfloat) -((OGL_Data->BoundBox.z[0] + OGL_Data->BoundBox.z[1]) / 2.0)
2739     );
2740     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
2741 }
2742 
2743 
2744 Boolean OGL_SetPosition3D(TOGL_Data * OGL_Data, Nlm_RectPtr rect)
2745 /* resizes and positions the 3D window */
2746 {
2747     Nlm_Int4 width = rect->right - rect->left + 1;
2748     Nlm_Int4 height = rect->bottom - rect->top + 1;
2749 
2750     if (OGL_Data == NULL || rect == NULL)
2751         return FALSE;
2752 
2753     if (OGL_Data->Z_rotate) {
2754         rect->top += Nlm_hScrollBarHeight;
2755         height -= Nlm_hScrollBarHeight;
2756     }
2757 
2758     if (Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel))
2759         width -= Nlm_vScrollBarWidth + 3;
2760     if (Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel))
2761         height -= Nlm_hScrollBarHeight + 3;
2762 
2763     if (width < 16 || height < 16)
2764         return FALSE;
2765 
2766 #if defined(WIN_MOTIF) && defined(MESA)
2767     if (glXGetCurrentContext())
2768 #endif
2769         glViewport(0, 0, width, height);
2770 
2771     rect->right = (Nlm_Int2) (rect->left + width + 3);
2772     rect->bottom = (Nlm_Int2) (rect->top + height + 3);
2773     if (Nlm_GetSlateVScrollBar((Nlm_SlatE) OGL_Data->Panel))
2774         rect->right += Nlm_vScrollBarWidth;
2775     if (Nlm_GetSlateHScrollBar((Nlm_SlatE) OGL_Data->Panel))
2776         rect->bottom += Nlm_hScrollBarHeight;
2777 
2778     Nlm_SetPosition(OGL_Data->Panel, rect); /* resize the panel */
2779     Nlm_ProcessUpdatesFirst(FALSE);
2780     Nlm_AdjustPrnt(OGL_Data->Panel, rect, FALSE);
2781 
2782     if (OGL_Data->Z_rotate) {   /* if there is a z rotation scroll bar */
2783         rect->bottom = rect->top;
2784         rect->top -= Nlm_hScrollBarHeight;
2785         Nlm_SetPosition(OGL_Data->Z_rotate, rect);
2786     }
2787 
2788     return TRUE;
2789 }
2790 
2791 
2792 void OGL_ClearOGL_Data(TOGL_Data * OGL_Data)
2793 /* clear the transforms and bound box in OGL_Data structure */
2794 {
2795     if (OGL_Data == NULL)
2796         return;
2797 
2798     OGL_ClearBoundBox(&(OGL_Data->BoundBox));
2799     OGL_Data->MaxSize = 2.0 * OGL_DEFAULT_SIZE;
2800     OGL_Data->Background.rgb[0] = 0;
2801     OGL_Data->Background.rgb[1] = 0;
2802     OGL_Data->Background.rgb[2] = 0;
2803 }
2804 
2805 
2806 void OGL_ClearBoundBox(TOGL_BoundBox * BoundBox)
2807 /* initialize a bounds box */
2808 {
2809     Nlm_Int4 i;
2810 
2811     if (BoundBox == NULL)
2812         return;
2813     for (i = 0; i < 2; i++) {
2814         BoundBox->x[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2815         BoundBox->y[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2816         BoundBox->z[i] = (Nlm_FloatLo) ((i * 2 - 1) * OGL_DEFAULT_SIZE);
2817     }
2818     BoundBox->set = TRUE;       /* we've set up the default values */
2819 }
2820 
2821 /* function to start, stop, and check if animation is playing */
2822 
2823 NLM_EXTERN Nlm_Boolean OGL_IsPlaying(TOGL_Data *pOGL_Data)
2824 {
2825 #ifdef WIN_MAC
2826     return FALSE;
2827 #else
2828     return pOGL_Data->IsPlaying;
2829 #endif
2830 }
2831 
2832 NLM_EXTERN void OGL_StopPlaying(TOGL_Data *pOGL_Data)
2833 {
2834 #ifndef WIN_MAC
2835     pOGL_Data->IsPlaying = FALSE;
2836 #endif
2837 }
2838 
2839 NLM_EXTERN void OGL_StartPlaying(TOGL_Data *pOGL_Data)
2840 {
2841 #ifndef WIN_MAC
2842     Nlm_StopWatchPtr pStopwatch;
2843     Nlm_FloatHi newtime;
2844 
2845     pOGL_Data->IsPlaying = TRUE;
2846     pStopwatch = Nlm_StopWatchNew();
2847     Nlm_StopWatchStart(pStopwatch);
2848     while (!Nlm_QuittingProgram() && OGL_IsPlaying(pOGL_Data)) {
2849         OGL_Play(pOGL_Data);
2850         OGL_DrawViewer3D(pOGL_Data);
2851         if(pOGL_Data->Tick != 0.0L) {
2852             do {
2853                 Nlm_StopWatchStop(pStopwatch);
2854                 newtime = Nlm_GetElapsedTime(pStopwatch);
2855                 if (Nlm_EventAvail()) Nlm_ProcessAnEvent();
2856             } while (newtime < pOGL_Data->Tick);
2857             Nlm_StopWatchStart(pStopwatch);
2858         }
2859         while (Nlm_EventAvail())
2860             Nlm_ProcessAnEvent();
2861     }
2862     Nlm_StopWatchFree(pStopwatch);
2863 #endif
2864 }
2865 
2866 
2867 #ifdef _PNG
2868 
2869 /* callback used by PNG library to report errors */
2870 static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
2871 {
2872     Message(MSG_ERROR, "PNG Error: %s", msg);
2873 }
2874 
2875 #ifdef WIN_MOTIF
2876 static Nlm_Boolean gotAnXError;
2877 
2878 int OGL_XErrorHandler(Display *dpy, XErrorEvent *error)
2879 {
2880     gotAnXError = TRUE;
2881     return 0;
2882 }
2883 #endif /* WIN_MOTIF */
2884 
2885 static Nlm_MonitorPtr row_monitor;
2886 static int nRows;
2887 
2888 void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
2889 {
2890     char message[256];
2891     float progress;
2892 
2893     if (nRows < 0) { /* if negative, then we're doing interlacing */
2894         float start, end;
2895         switch (pass + 1) {
2896             case 1: start = 0;  end = 1;  break;
2897             case 2: start = 1;  end = 2;  break;
2898             case 3: start = 2;  end = 3;  break;
2899             case 4: start = 3;  end = 5;  break;
2900             case 5: start = 5;  end = 7;  break;
2901             case 6: start = 7;  end = 11; break;
2902             case 7: start = 11; end = 15; break;
2903         }
2904         progress = 100.0 * (
2905             (start / 15) +
2906             (((float) row) / (-nRows)) * ((end - start) / 15)
2907         );
2908     } else { /* not interlaced */
2909         progress = 100.0 * row / nRows;
2910     }
2911     sprintf(message, "Writing PNG file (%i%%) ...", (int) progress);
2912     Nlm_MonitorStrValue(row_monitor, message);
2913     Nlm_Update();
2914 }
2915 
2916 /* saves current OpenGL context window to PNG file */
2917 void Nlm_SaveImagePNG(Nlm_Char *fname)
2918 {
2919     GLint viewport[4];
2920     GLboolean isRGBA;
2921     Nlm_Uchar *row = NULL;
2922     FILE *out = NULL;
2923     int i, bytesPerPixel = 3;
2924     png_structp png_ptr = NULL;
2925     png_infop info_ptr = NULL;
2926     Nlm_Boolean doInterlacing = TRUE;
2927     TOGL_Data *OGL_Data = (TOGL_Data *)(Cn3D_GetCurrentOGLData());
2928 
2929 #if defined(WIN_MOTIF)
2930     GLint glSize;
2931     int nAttribs, attribs[20];
2932     XVisualInfo *visinfo;
2933     Nlm_Boolean localVI = FALSE;
2934     Pixmap xPixmap;
2935     GLXContext currentCtx, glCtx = NULL;
2936     GLXPixmap glxPixmap;
2937     GLXDrawable currentXdrw;
2938     extern Display *Nlm_currentXDisplay;
2939     extern int Nlm_currentXScreen;
2940     int (*currentXErrHandler)(Display *, XErrorEvent *);
2941 
2942 #elif defined(WIN_MSWIN)
2943     HDC current_hdc = NULL, hdc = NULL;
2944     HGDIOBJ current_hgdiobj = NULL;
2945     HBITMAP hbm = NULL;
2946     PIXELFORMATDESCRIPTOR pfd;
2947     int nPixelFormat;
2948     HGLRC hglrc = NULL, current_hglrc = NULL;
2949 
2950 #elif defined(WIN_MAC)
2951         Nlm_Uchar *base = NULL;
2952     GLint attrib[20], glSize;
2953     int na = 0;
2954     AGLPixelFormat fmt = NULL;
2955     AGLContext ctx = NULL, currentCtx;
2956 
2957 #endif
2958 
2959     glGetBooleanv(GL_RGBA_MODE, &isRGBA);
2960     if (!isRGBA) {
2961         /* turn this off till I figure out good way to retrieve colormap */
2962         Message(MSG_ERROR, "8-Bit PNG output currently unavailable");
2963         return;
2964     }
2965 
2966     /* determine the size of the window and allocate (platform-dependent)
2967        off-screen rendering area */
2968     glGetIntegerv(GL_VIEWPORT, viewport);
2969     /*
2970     viewport[2] *= 3;
2971     viewport[3] *= 3;
2972     */
2973 
2974 #if defined(WIN_MOTIF)
2975     currentCtx = glXGetCurrentContext(); /* save current context info */
2976     currentXdrw = glXGetCurrentDrawable();
2977 
2978     currentXErrHandler = XSetErrorHandler(OGL_XErrorHandler);
2979     gotAnXError = FALSE;
2980 
2981     /* first, try to get a non-doublebuffered visual, to economize on memory */
2982     nAttribs = 0;
2983     attribs[nAttribs++] = GLX_USE_GL;
2984     attribs[nAttribs++] = GLX_RGBA;
2985     attribs[nAttribs++] = GLX_RED_SIZE;
2986     glGetIntegerv(GL_RED_BITS, &glSize);
2987     attribs[nAttribs++] = glSize;
2988     attribs[nAttribs++] = GLX_GREEN_SIZE;
2989     attribs[nAttribs++] = glSize;
2990     attribs[nAttribs++] = GLX_BLUE_SIZE;
2991     attribs[nAttribs++] = glSize;
2992     attribs[nAttribs++] = GLX_DEPTH_SIZE;
2993     glGetIntegerv(GL_DEPTH_BITS, &glSize);
2994     attribs[nAttribs++] = glSize;
2995     attribs[nAttribs++] = None;
2996     visinfo = glXChooseVisual((Display *) OGL_Data->display,
2997                               DefaultScreen((Display *) OGL_Data->display),
2998                               attribs);
2999 
3000     /* if that fails, just revert to the one used for the regular window */
3001     if (visinfo)
3002         localVI = TRUE;
3003     else
3004         visinfo = (XVisualInfo *) (OGL_Data->visinfo);
3005 
3006     xPixmap = XCreatePixmap((Display *) OGL_Data->display,
3007         RootWindow((Display *) OGL_Data->display, Nlm_currentXScreen),
3008         viewport[2], viewport[3], visinfo->depth);
3009     glxPixmap = glXCreateGLXPixmap((Display *) OGL_Data->display,
3010         visinfo, xPixmap);
3011     if (gotAnXError) {
3012         Message(MSG_ERROR, "Got an X error creating GLXPixmap context");
3013         goto cleanup;
3014     }
3015     glCtx = glXCreateContext((Display *) OGL_Data->display,
3016         visinfo,
3017         currentCtx, /* share display list with "regular" context */
3018         GL_FALSE);
3019     if (!glCtx || !glXMakeCurrent((Display *) OGL_Data->display,
3020                                   glxPixmap, glCtx)) {
3021         Message(MSG_ERROR, "Failed to make current GLXPixmap rendering context");
3022         goto cleanup;
3023     }
3024 
3025 #elif defined(WIN_MSWIN)
3026     current_hglrc = wglGetCurrentContext(); /* save to restore later */
3027     current_hdc = wglGetCurrentDC();
3028 
3029     /* create bitmap of same color type as current rendering context */
3030     hbm = CreateCompatibleBitmap(current_hdc, viewport[2], viewport[3]);
3031     if (!hbm) {
3032         Message(MSG_ERROR, "Failed to create rendering BITMAP");
3033         goto cleanup;
3034     }
3035     memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
3036     pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
3037     pfd.nVersion = 1;
3038     /* NOT doublebuffered, to save memory */
3039     pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL;
3040     pfd.iPixelType = PFD_TYPE_RGBA;
3041     pfd.cColorBits = GetDeviceCaps(current_hdc, BITSPIXEL);
3042     pfd.iLayerType = PFD_MAIN_PLANE;
3043 
3044     hdc = CreateCompatibleDC(current_hdc);
3045     if (!hdc) {
3046         Message(MSG_ERROR, "CreateCompatibleDC failed");
3047         goto cleanup;
3048     }
3049 
3050     current_hgdiobj = SelectObject(hdc, hbm);
3051     if (!current_hgdiobj) {
3052         Message(MSG_ERROR, "SelectObject failed");
3053         goto cleanup;
3054     }
3055     nPixelFormat = ChoosePixelFormat(hdc, &pfd);
3056     if (!nPixelFormat) {
3057         Message(MSG_ERROR, "ChoosePixelFormat failed: %i", GetLastError());
3058         goto cleanup;
3059     }
3060     if (!SetPixelFormat(hdc, nPixelFormat, &pfd)) {
3061         Message(MSG_ERROR, "SetPixelFormat failed: %i", GetLastError());
3062         goto cleanup;
3063     }
3064     /*DescribePixelFormat(hdc, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);*/
3065     hglrc = wglCreateContext(hdc);
3066     if (!hglrc) {
3067         Message(MSG_ERROR, "wglCreateContext failed: %i", GetLastError());
3068         goto cleanup;
3069     }
3070     /* need to share display lists with regular window */
3071     if (!wglShareLists(current_hglrc, hglrc)) {
3072         Message(MSG_ERROR, "wglShareLists failed: %i", GetLastError());
3073         goto cleanup;
3074     }
3075     if (!wglMakeCurrent(hdc, hglrc)) {
3076         Message(MSG_ERROR, "wglMakeCurrent failed: %i", GetLastError());
3077         goto cleanup;
3078     }
3079 
3080 #elif defined(WIN_MAC)
3081         currentCtx = aglGetCurrentContext();
3082 
3083         /* Mac pixels seem to always be 32-bit */
3084         bytesPerPixel = 4;
3085 
3086         base = (Nlm_Uchar *) MemNew(viewport[2] * viewport[3] * bytesPerPixel);
3087         if (!base) {
3088         Message(MSG_ERROR, "Failed to allocate image buffer");
3089         goto cleanup;
3090         }
3091 
3092         /* create an off-screen rendering context (NOT doublebuffered) */
3093         attrib[na++] = AGL_OFFSCREEN;
3094         attrib[na++] = AGL_RGBA;
3095         glGetIntegerv(GL_RED_BITS, &glSize);
3096     attrib[na++] = AGL_RED_SIZE;
3097     attrib[na++] = glSize;
3098     attrib[na++] = AGL_GREEN_SIZE;
3099     attrib[na++] = glSize;
3100     attrib[na++] = AGL_BLUE_SIZE;
3101     attrib[na++] = glSize;
3102         glGetIntegerv(GL_DEPTH_BITS, &glSize);
3103     attrib[na++] = AGL_DEPTH_SIZE;
3104     attrib[na++] = glSize;
3105     attrib[na++] = AGL_NONE;
3106 
3107     if ((fmt=aglChoosePixelFormat(NULL, 0, attrib)) == NULL) {
3108         Message(MSG_ERROR, "aglChoosePixelFormat failed");
3109         goto cleanup;
3110     }
3111     /* share display lists with current "regular" context */
3112     if ((ctx=aglCreateContext(fmt, currentCtx)) == NULL) {
3113         Message(MSG_ERROR, "aglCreateContext failed");
3114         goto cleanup;
3115     }
3116 
3117     /* attach off-screen buffer to this context */
3118     if (!aglSetOffScreen(ctx, viewport[2], viewport[3],
3119                          bytesPerPixel * viewport[2], base)) {
3120         Message(MSG_ERROR, "aglSetOffScreen failed");
3121         goto cleanup;
3122         }
3123     if (!aglSetCurrentContext(ctx)) {
3124         Message(MSG_ERROR, "aglSetCurrentContext failed");
3125         goto cleanup;
3126         }
3127 
3128 #endif
3129 
3130     row = (Nlm_Uchar *) MemNew(viewport[2] * bytesPerPixel);
3131     if (!row) {
3132         Message(MSG_ERROR, "Failed to allocate pixel storage for PNG output");
3133         goto cleanup;
3134     }
3135 
3136     /* open the output file for writing */
3137     out = fopen(fname, "wb");
3138     if (!out) {
3139         Message(MSG_ERROR, "Can't write to file '%s'", fname);
3140         goto cleanup;
3141     }
3142 
3143     /* set up the PNG writing (see libpng.txt) */
3144     png_ptr = png_create_write_struct(
3145         PNG_LIBPNG_VER_STRING, NULL, writepng_error_handler, NULL);
3146     if (!png_ptr) {
3147         Message(MSG_ERROR, "Can't create PNG write structure");
3148         goto cleanup;
3149     }
3150 
3151     info_ptr = png_create_info_struct(png_ptr);
3152     if (!info_ptr) {
3153         Message(MSG_ERROR, "Can't create PNG info structure");
3154         goto cleanup;
3155     }
3156 
3157     if (setjmp(png_ptr->jmpbuf)) {
3158         Message(MSG_ERROR, "PNG write failed");
3159         goto cleanup;
3160     }
3161 
3162     png_init_io(png_ptr, out);
3163 
3164     /* sets callback that's called by PNG after each written row */
3165     png_set_write_status_fn(png_ptr, write_row_callback);
3166 
3167     png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
3168 
3169     png_set_IHDR(png_ptr, info_ptr, viewport[2], viewport[3],
3170         8, PNG_COLOR_TYPE_RGB,
3171         doInterlacing ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
3172         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
3173 
3174     png_write_info(png_ptr, info_ptr);
3175 
3176     /*
3177        Redraw the model into the new off-screen context, then use glReadPixels
3178        to retrieve pixel data. It's much easier to use glReadPixels rather than
3179        trying to read directly from the off-screen buffer, since GL can do all
3180        the potentially tricky work of translating from whatever pixel format
3181        the buffer uses into "regular" RGB byte triples.
3182     */
3183     row_monitor = Nlm_MonitorStrNewEx("PNG Progress", 20, FALSE);
3184     Nlm_MonitorStrValue(row_monitor, "Rendering to off-screen buffer...");
3185     Nlm_Update();
3186     OGL_Data->NeedCameraSetup = TRUE;
3187 #ifdef WIN_MAC
3188     drawingOffscreen = TRUE; /* signal redraw not to use agl buffer rect */
3189 #endif
3190     OGL_DrawViewer3D(OGL_Data);
3191 #ifdef WIN_MAC
3192     drawingOffscreen = FALSE;
3193 #endif
3194     glPixelStorei(GL_PACK_ALIGNMENT, 1);
3195 
3196     /*
3197        Write image row by row to avoid having to allocate another full image's
3198        worth of memory. Do multiple passes for interlacing, if necessary. Note
3199        that PNG's rows are stored top down, while GL's are bottom up.
3200     */
3201     nRows = viewport[3];
3202     if (doInterlacing) {
3203         int pass, r;
3204         nRows = -nRows; /* signal to monitor that we're interlacing */
3205         if (png_set_interlace_handling(png_ptr) != 7) {
3206             Message(MSG_ERROR, "confused by unkown PNG interlace scheme");
3207             goto cleanup;
3208         }
3209         for (pass = 1; pass <= 7; pass++) {
3210             for (i = viewport[3] - 1; i >= 0; i--) {
3211                 r = viewport[3] - i - 1;
3212                 /* when interlacing, only certain rows are actually read
3213                    during certain passes - avoid unncessary reads */
3214                 if (
3215                     ((pass == 1 || pass == 2) && (r % 8 == 0)) ||
3216                     ((pass == 3) && ((r - 4) % 8 == 0)) ||
3217                     ((pass == 4) && (r % 4 == 0)) ||
3218                     ((pass == 5) && ((r - 2) % 4 == 0)) ||
3219                     ((pass == 6) && (r % 2 == 0)) ||
3220                     ((pass == 7) && ((r - 1) % 2 == 0))
3221                    )
3222                     glReadPixels(viewport[0], i, viewport[2], 1,
3223                                  GL_RGB, GL_UNSIGNED_BYTE, row);
3224                 /* ... but still have to call this for each row in each pass */
3225                 png_write_row(png_ptr, row);
3226             }
3227         }
3228     } else { /* not interlaced */
3229         for (i = viewport[3] - 1; i >= 0; i--) {
3230             glReadPixels(viewport[0], i, viewport[2], 1,
3231                          GL_RGB, GL_UNSIGNED_BYTE, row);
3232             png_write_row(png_ptr, row);
3233         }
3234     }
3235     Nlm_MonitorFree(row_monitor);
3236     Nlm_Update();
3237 
3238     /* finish up */
3239     png_write_end(png_ptr, info_ptr);
3240 
3241 
3242     /* restore context and clean up */
3243     cleanup:
3244 
3245     if (out) fclose(out);
3246 
3247 #if defined(WIN_MOTIF)
3248     gotAnXError = FALSE;
3249     if (glCtx) {
3250         glXMakeCurrent(Nlm_currentXDisplay, currentXdrw, currentCtx);
3251         glXDestroyContext(Nlm_currentXDisplay, glCtx);
3252     }
3253     glXDestroyGLXPixmap(Nlm_currentXDisplay, glxPixmap);
3254     XFreePixmap(Nlm_currentXDisplay, xPixmap);
3255     if (localVI && visinfo) XFree(visinfo);
3256     if (gotAnXError) {
3257         Message(MSG_ERROR, "Got an X error destroying GLXPixmap context");
3258     }
3259     XSetErrorHandler(currentXErrHandler);
3260 
3261 #elif defined(WIN_MSWIN)
3262     if (current_hdc && current_hglrc) wglMakeCurrent(current_hdc, current_hglrc);
3263     if (hglrc) wglDeleteContext(hglrc);
3264     /*if (current_hgdiobj) SelectObject(hdc, current_hgdiobj);*/
3265     if (hbm) DeleteObject(hbm);
3266     if (hdc) DeleteDC(hdc);
3267 
3268 #elif defined(WIN_MAC)
3269         aglSetCurrentContext(currentCtx);
3270         if (ctx) aglDestroyContext(ctx);
3271         if (fmt) aglDestroyPixelFormat(fmt);
3272         if (base) MemFree(base);
3273 
3274 #endif
3275 
3276     if (row) MemFree(row);
3277     if (png_ptr) {
3278         if (info_ptr)
3279             png_destroy_write_struct(&png_ptr, &info_ptr);
3280         else
3281             png_destroy_write_struct(&png_ptr, NULL);
3282     }
3283 
3284     /* redraw in case progress meter overwrote part of GL area */
3285     OGL_DrawViewer3D(OGL_Data);
3286 }
3287 
3288 #endif /* _PNG */
3289 
3290 
3291 /* create display list with logo */
3292 NLM_EXTERN void OGL_DrawLogo(TOGL_Data *OGL_Data)
3293 {
3294     DDV_ColorCell colorCell;
3295     Uint1 logoColor[3] = { 100, 240, 150 };
3296     int i, n, s, g;
3297 
3298 #define LOGO_SIDES 36
3299     int segments = 180;
3300     GLdouble bigRad = 12.0, height = 24.0,
3301         minRad = 0.1, maxRad = 2.0,
3302         ringPts[LOGO_SIDES * 3], *pRingPts = ringPts,
3303         prevRing[LOGO_SIDES * 3], *pPrevRing = prevRing, *tmp,
3304         ringNorm[LOGO_SIDES * 3], *pRingNorm = ringNorm,
3305         prevNorm[LOGO_SIDES * 3], *pPrevNorm = prevNorm,
3306         PI = acos(-1), length,
3307         startRad, midRad, phase, currentRad, CR[3], H[3], V[3];
3308 
3309     if (!OGL_Data || OGL_Data->IndexMode) return;
3310 
3311     /* set up initial camera */
3312     OGL_Data->CameraDistance = 200.0;
3313     OGL_Data->CameraDirection[0] = 0.0;
3314     OGL_Data->CameraDirection[1] = 0.0;
3315     OGL_Data->CameraAngle = acos(-1) / 14;
3316     OGL_Data->NeedCameraSetup = TRUE;
3317     glMatrixMode(GL_MODELVIEW);
3318     glLoadIdentity();
3319     glGetDoublev(GL_MODELVIEW_MATRIX, (GLdouble *) OGL_Data->ModelMatrix);
3320 
3321     glNewList(1, GL_COMPILE);
3322 
3323     /* create logo */
3324     DDV_SetColorInCell(&colorCell, logoColor);
3325     OGL_SetColor(OGL_Data, &colorCell, GL_DIFFUSE, 1.0);
3326 
3327     for (n = 0; n < 2; n++) { /* helix strand */
3328         if (n == 0) {
3329             startRad = maxRad;
3330             midRad = minRad;
3331             phase = 0;
3332         } else {
3333             startRad = minRad;
3334             midRad = maxRad;
3335             phase = PI;
3336         }
3337         for (g = 0; g <= segments; g++) { /* segment (bottom to top) */
3338 
3339             if (g < segments/2)
3340                 currentRad = startRad + (midRad - startRad) *
3341                     (0.5 - 0.5 * cos(PI * g / (segments/2)));
3342             else
3343                 currentRad = midRad + (startRad - midRad) *
3344                     (0.5 - 0.5 * cos(PI * (g - segments/2) / (segments/2)));
3345 
3346             CR[1] = height * g / segments - height/2;
3347             if (g > 0) phase += PI * 2 / segments;
3348             CR[2] = bigRad * cos(phase);
3349             CR[0] = bigRad * sin(phase);
3350 
3351             /* make a strip around the strand circumference */
3352             for (s = 0; s < LOGO_SIDES; s++) {
3353                 V[0] = CR[0];
3354                 V[2] = CR[2];
3355                 V[1] = 0;
3356                 length = sqrt(V[0]*V[0] + V[1]*V[1] + V[2]*V[2]);
3357                 for (i = 0; i < 3; i++) V[i] /= length;
3358                 H[0] = H[2] = 0;
3359                 H[1] = 1;
3360                 for (i = 0; i < 3; i++) {
3361                     pRingNorm[3*s + i] = V[i] * cos(PI * 2 * s / LOGO_SIDES) +
3362                                          H[i] * sin(PI * 2 * s / LOGO_SIDES);
3363                     pRingPts[3*s + i] = CR[i] + pRingNorm[3*s + i] * currentRad;
3364                 }
3365             }
3366             if (g > 0) {
3367                 glBegin(GL_TRIANGLE_STRIP);
3368                 for (s = 0; s < LOGO_SIDES; s++) {
3369                     glNormal3d(pPrevNorm[3*s], pPrevNorm[3*s + 1], pPrevNorm[3*s + 2]);
3370                     glVertex3d(pPrevRing[3*s], pPrevRing[3*s + 1], pPrevRing[3*s + 2]);
3371                     glNormal3d(pRingNorm[3*s], pRingNorm[3*s + 1], pRingNorm[3*s + 2]);
3372                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3373                 }
3374                 glNormal3d(pPrevNorm[0], pPrevNorm[1], pPrevNorm[2]);
3375                 glVertex3d(pPrevRing[0], pPrevRing[1], pPrevRing[2]);
3376                 glNormal3d(pRingNorm[0], pRingNorm[1], pRingNorm[2]);
3377                 glVertex3d(pRingPts[0], pRingPts[1], pRingPts[2]);
3378                 glEnd();
3379             }
3380             
3381             /* cap the ends */
3382             glBegin(GL_POLYGON);
3383             if ((g == 0 && n == 0) || (g == segments && n == 1))
3384                 glNormal3d(-1, 0, 0);
3385             else
3386                 glNormal3d(1, 0, 0);
3387             if (g == 0) {
3388                 for (s = 0; s < LOGO_SIDES; s++)
3389                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3390             } else if (g == segments) {
3391                 for (s = LOGO_SIDES - 1; s >= 0; s--)
3392                     glVertex3d(pRingPts[3*s], pRingPts[3*s + 1], pRingPts[3*s + 2]);
3393             }
3394             glEnd();
3395 
3396             /* switch pointers to store previous ring */
3397             tmp = pPrevRing;
3398             pPrevRing = pRingPts;
3399             pRingPts = tmp;
3400             tmp = pPrevNorm;
3401             pPrevNorm = pRingNorm;
3402             pRingNorm = tmp;
3403         }
3404     }
3405 
3406     glEndList();
3407 
3408     OGL_Data->Layers->SelectedLayer = 1; /* fool DrawViewer3D into thinking there's a single structure */
3409     OGL_DrawViewer3D(OGL_Data);
3410 }
3411 
3412 #endif                          /* _OPENGL */
3413 
3414 
3415 /* this function "shared" by both vibrant and OpenGL versions */
3416 #ifdef _OPENGL
3417 /* add a thick splined curve from point 1 *halfway* to point 2 */
3418 void Nlm_AddHalfWorm3D(TOGL_Data * OGL_Data, DDV_ColorCell * color,
3419                        Nlm_FloatHi x0, Nlm_FloatHi y0, Nlm_FloatHi z0,
3420                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
3421                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
3422                        Nlm_FloatHi x3, Nlm_FloatHi y3, Nlm_FloatHi z3,
3423                        Nlm_Boolean cap1, Nlm_Boolean cap2, Nlm_FloatHi radius,
3424                        Nlm_Int4 segments, Nlm_Int4 sides)
3425 #else
3426 typedef Nlm_FloatLo GLfloat;
3427 typedef Nlm_FloatHi GLdouble;
3428 void Nlm_AddHalfWorm3D(Nlm_Picture3D pic,
3429                        Nlm_Segment3D segment, BigScalar userData,
3430                        Uint1 layer, Uint1 color,
3431                        Nlm_FloatHi x0, Nlm_FloatHi y0, Nlm_FloatHi z0,
3432                        Nlm_FloatHi x1, Nlm_FloatHi y1, Nlm_FloatHi z1,
3433                        Nlm_FloatHi x2, Nlm_FloatHi y2, Nlm_FloatHi z2,
3434                        Nlm_FloatHi x3, Nlm_FloatHi y3, Nlm_FloatHi z3,
3435                        Nlm_FloatHi radius, Nlm_Int4 segments)
3436 #endif
3437 {
3438     Nlm_Int4 i, j, k, m, offset;
3439     GLdouble len, R1x, R1y, R1z, R2x, R2y, R2z, MG[4][3],
3440         T[4], t, Qtx, Qty, Qtz, px, py, pz,
3441         dQtx, dQty, dQtz, /*ddQtx, ddQty, ddQtz,*/
3442         Hx, Hy, Hz, Vx, Vy, Vz, prevlen,
3443         cosj, sinj, pi = 3.14159265358979323846;
3444     GLdouble *Nx = NULL, *Ny, *Nz, *Cx, *Cy, *Cz,
3445         *pNx, *pNy, *pNz, *pCx, *pCy, *pCz, *tmp, *fblock = NULL;
3446     /*
3447      * The Hermite matrix Mh.
3448      */
3449     static Nlm_FloatHi Mh[4][4] = {
3450         { 2, -2,  1,  1},
3451         {-3,  3, -2, -1},
3452         { 0,  0,  1,  0},
3453         { 1,  0,  0,  0}
3454     };
3455     /*
3456      * Variables that affect the curve shape
3457      *   a=b=0 = Catmull-Rom
3458      */
3459     static Nlm_FloatHi a = -0.8, /* tension    (adjustable)  */
3460      c = 0,                     /* continuity (should be 0) */
3461      b = 0;                     /* bias       (should be 0) */
3462 
3463 #ifdef _OPENGL
3464     if (radius > 0.0) {         /* if line, color assigned in OGL_AddLine3D */
3465         if (OGL_Data == NULL || color == NULL)
3466             return;
3467 
3468         OGL_SetColor(OGL_Data, color, GL_DIFFUSE, 1.0);
3469 
3470         if (sides % 2)
3471             sides++;         /* must be an even number! */
3472     }
3473 #endif
3474 
3475     /* First, calculate the coordinate points of the center of the worm,
3476      * using the Kochanek-Bartels variant of the Hermite curve.
3477      */
3478     R1x = 0.5 * (1 - a) * (1 + b) * (1 + c) * (x1 - x0) +
3479         0.5 * (1 - a) * (1 - b) * (1 - c) * (x2 - x1);
3480     R1y = 0.5 * (1 - a) * (1 + b) * (1 + c) * (y1 - y0) +
3481         0.5 * (1 - a) * (1 - b) * (1 - c) * (y2 - y1);
3482     R1z = 0.5 * (1 - a) * (1 + b) * (1 + c) * (z1 - z0) +
3483         0.5 * (1 - a) * (1 - b) * (1 - c) * (z2 - z1);
3484     R2x = 0.5 * (1 - a) * (1 + b) * (1 - c) * (x2 - x1) +
3485         0.5 * (1 - a) * (1 - b) * (1 + c) * (x3 - x2);
3486     R2y = 0.5 * (1 - a) * (1 + b) * (1 - c) * (y2 - y1) +
3487         0.5 * (1 - a) * (1 - b) * (1 + c) * (y3 - y2);
3488     R2z = 0.5 * (1 - a) * (1 + b) * (1 - c) * (z2 - z1) +
3489         0.5 * (1 - a) * (1 - b) * (1 + c) * (z3 - z2);
3490     /*
3491      * Multiply MG=Mh.Gh, where Gh = [ P(1) P(2) R(1) R(2) ]. This
3492      * 4x1 matrix of vectors is constant for each segment.
3493      */
3494     for (i = 0; i < 4; i++) {   /* calculate Mh.Gh */
3495         MG[i][0] =
3496             Mh[i][0] * x1 + Mh[i][1] * x2 + Mh[i][2] * R1x +
3497             Mh[i][3] * R2x;
3498         MG[i][1] =
3499             Mh[i][0] * y1 + Mh[i][1] * y2 + Mh[i][2] * R1y +
3500             Mh[i][3] * R2y;
3501         MG[i][2] =
3502             Mh[i][0] * z1 + Mh[i][1] * z2 + Mh[i][2] * R1z +
3503             Mh[i][3] * R2z;
3504     }
3505 
3506     for (i = 0; i <= segments; i++) {
3507 
3508         /* t goes from [0,1] from P(1) to P(2) (and we want to go halfway only),
3509            and the function Q(t) defines the curve of this segment. */
3510         t = (0.5 / segments) * i;
3511         /*
3512            * Q(t)=T.(Mh.Gh), where T = [ t^3 t^2 t 1 ]
3513          */
3514         T[0] = t * t * t;
3515         T[1] = t * t;
3516         T[2] = t;
3517         T[3] = 1;
3518         Qtx = T[0] * MG[0][0] + T[1] * MG[1][0] + T[2] * MG[2][0] + MG[3][0] /* *T[3] */
3519             ;
3520         Qty = T[0] * MG[0][1] + T[1] * MG[1][1] + T[2] * MG[2][1] + MG[3][1] /* *T[3] */
3521             ;
3522         Qtz = T[0] * MG[0][2] + T[1] * MG[1][2] + T[2] * MG[2][2] + MG[3][2] /* *T[3] */
3523             ;
3524 
3525 #ifndef _OPENGL
3526         if (i > 0) {
3527             Int4 iX0, iY0, iZ0, iX1, iY1, iZ1;
3528 
3529 /* number 1000000.0 must be same as VIEWSCALE in algorend.c! */
3530             iX0 = (Int4) (px * 1000000.0);
3531             iY0 = (Int4) (py * 1000000.0);
3532             iZ0 = (Int4) (pz * 1000000.0);
3533             iX1 = (Int4) (Qtx * 1000000.0);
3534             iY1 = (Int4) (Qty * 1000000.0);
3535             iZ1 = (Int4) (Qtz * 1000000.0);
3536             AddLine3D(pic, segment, userData, layer, color,
3537                       iX0, iY0, iZ0, iX1, iY1, iZ1);
3538         }
3539         /* save to use as previous point for connecting points together */
3540         px = Qtx;
3541         py = Qty;
3542         pz = Qtz;
3543 
3544 #else
3545         if (radius == 0.0) {
3546             if (i > 0)
3547                 OGL_AddLine3D(OGL_Data, color, px, py, pz, Qtx, Qty, Qtz);
3548             /* save to use as previous point for connecting points together */
3549             px = Qtx;
3550             py = Qty;
3551             pz = Qtz;
3552 
3553         } else {
3554             /* construct a circle of points centered at and
3555                in a plane normal to the curve at t - these points will
3556                be used to construct the "thick" worm */
3557 
3558             /* allocate single block of storage for two circles of points */
3559             if (!Nx) {
3560                 fblock = Nx = MemNew(sizeof(Nlm_FloatHi) * 12 * sides);
3561                 if (!Nx)
3562                     return;
3563                 Ny = &Nx[sides];
3564                 Nz = &Nx[sides * 2];
3565                 Cx = &Nx[sides * 3];
3566                 Cy = &Nx[sides * 4];
3567                 Cz = &Nx[sides * 5];
3568                 pNx = &Nx[sides * 6];
3569                 pNy = &Nx[sides * 7];
3570                 pNz = &Nx[sides * 8];
3571                 pCx = &Nx[sides * 9];
3572                 pCy = &Nx[sides * 10];
3573                 pCz = &Nx[sides * 11];
3574             }
3575 
3576             /*
3577              * The first derivative of Q(t), d(Q(t))/dt, is the slope
3578              * (tangent) at point Q(t); now T = [ 3t^2 2t 1 0 ]
3579              */
3580             T[0] = t * t * 3;
3581             T[1] = t * 2;
3582             T[2] = 1;
3583             T[3] = 0;
3584             dQtx =
3585                 T[0] * MG[0][0] + T[1] * MG[1][0] +
3586                 MG[2][0] /* *T[2] + T[3]*MG[3][0] */ ;
3587             dQty =
3588                 T[0] * MG[0][1] + T[1] * MG[1][1] +
3589                 MG[2][1] /* *T[2] + T[3]*MG[3][1] */ ;
3590             dQtz =
3591                 T[0] * MG[0][2] + T[1] * MG[1][2] +
3592                 MG[2][2] /* *T[2] + T[3]*MG[3][2] */ ;
3593 
3594             /* use cross prod't of [1,0,0] x normal as horizontal */
3595             Hx = 0.0;
3596             Hy = -dQtz;
3597             Hz = dQty;
3598             len = sqrt(Hy * Hy + Hz * Hz);
3599             if (len < 0.000001) { /* nearly colinear - use [1,0.1,0] instead */
3600                 Hx = 0.1 * dQtz;
3601                 Hy = -dQtz;
3602                 Hz = dQty - 0.1 * dQtx;
3603             }
3604             Hx /= len;
3605             Hy /= len;
3606             Hz /= len;
3607 
3608             /* and a vertical vector = normal x H */
3609             Vx = dQty * Hz - dQtz * Hy;
3610             Vy = dQtz * Hx - dQtx * Hz;
3611             Vz = dQtx * Hy - dQty * Hx;
3612             len = sqrt(Vx * Vx + Vy * Vy + Vz * Vz);
3613             Vx /= len;
3614             Vy /= len;
3615             Vz /= len;
3616 
3617             /* finally, the worm circumference points (C) and normals (N) are
3618                simple trigonomic combinations of H and V */
3619             for (j = 0; j < sides; j++) {
3620                 cosj = cos(2 * pi * j / sides);
3621                 sinj = sin(2 * pi * j / sides);
3622                 Nx[j] = Hx * cosj + Vx * sinj;
3623                 Ny[j] = Hy * cosj + Vy * sinj;
3624                 Nz[j] = Hz * cosj + Vz * sinj;
3625                 Cx[j] = Qtx + Nx[j] * radius;
3626                 Cy[j] = Qty + Ny[j] * radius;
3627                 Cz[j] = Qtz + Nz[j] * radius;
3628             }
3629 
3630             /* figure out which points on the previous circle "match" best
3631                with these, to minimize envelope twisting */
3632             for (m = 0; m < sides; m++) {
3633                 len = 0.0;
3634                 for (j = 0; j < sides; j++) {
3635                     k = j + m;
3636                     if (k >= sides)
3637                         k -= sides;
3638                     len += (Cx[k] - pCx[j]) * (Cx[k] - pCx[j]) +
3639                         (Cy[k] - pCy[j]) * (Cy[k] - pCy[j]) +
3640                         (Cz[k] - pCz[j]) * (Cz[k] - pCz[j]);
3641                 }
3642                 if (m == 0 || len < prevlen) {
3643                     prevlen = len;
3644                     offset = m;
3645                 }
3646             }
3647 
3648             /* create triangles from points along this and previous circle */
3649             if (i > 0) {
3650                 glBegin(GL_TRIANGLE_STRIP);
3651                 for (j = 0; j < sides; j++) {
3652                     k = j + offset;
3653                     if (k >= sides) k -= sides;
3654                     glNormal3d(Nx[k], Ny[k], Nz[k]);
3655                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3656                     glNormal3d(pNx[j], pNy[j], pNz[j]);
3657                     glVertex3d(pCx[j], pCy[j], pCz[j]);
3658                 }
3659                 glNormal3d(Nx[offset], Ny[offset], Nz[offset]);
3660                 glVertex3d(Cx[offset], Cy[offset], Cz[offset]);
3661                 glNormal3d(pNx[0], pNy[0], pNz[0]);
3662                 glVertex3d(pCx[0], pCy[0], pCz[0]);
3663                 glEnd();
3664             }
3665 
3666             /* put caps on the end */
3667             if (cap1 && i == 0) {
3668                 glBegin(GL_POLYGON);
3669                 len = sqrt(dQtx*dQtx + dQty*dQty + dQtz*dQtz);
3670                 dQtx /= len; dQty /= len; dQtz /= len;
3671                 glNormal3d(-dQtx, -dQty, -dQtz);
3672                 for (j = sides - 1; j >= 0; j--) {
3673                     k = j + offset;
3674                     if (k >= sides) k -= sides;
3675                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3676                 }
3677                 glEnd();
3678             }
3679             else if (cap2 && i == segments) {
3680                 glBegin(GL_POLYGON);
3681                 len = sqrt(dQtx*dQtx + dQty*dQty + dQtz*dQtz);
3682                 dQtx /= len; dQty /= len; dQtz /= len;
3683                 glNormal3d(dQtx, dQty, dQtz);
3684                 for (j = 0; j < sides; j++) {
3685                     k = j + offset;
3686                     if (k >= sides) k -= sides;
3687                     glVertex3d(Cx[k], Cy[k], Cz[k]);
3688                 }
3689                 glEnd();
3690             }
3691 
3692             /* store this circle as previous for next round; instead of copying
3693                all values, just swap pointers */
3694 #define SWAPPTR(p1,p2) tmp=(p1); (p1)=(p2); (p2)=tmp
3695             SWAPPTR(Nx, pNx);
3696             SWAPPTR(Ny, pNy);
3697             SWAPPTR(Nz, pNz);
3698             SWAPPTR(Cx, pCx);
3699             SWAPPTR(Cy, pCy);
3700             SWAPPTR(Cz, pCz);
3701         }
3702 #endif
3703 
3704     }
3705     if (fblock)
3706         MemFree(fblock);
3707 }
3708 
3709 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.