1 /* gears, Copyright (c) 2007-2019 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Originally written by Brian Paul in 1996 or earlier;
12  * rewritten by jwz in Nov 2007.
13  */
14 
15 #define DEFAULTS	"*delay:	30000       \n" \
16 			"*count:        0           \n" \
17 			"*showFPS:      False       \n" \
18 			"*wireframe:    False       \n" \
19 			"*suppressRotationAnimation: True\n" \
20 
21 # define release_gears 0
22 #undef countof
23 #define countof(x) (sizeof((x))/sizeof((*x)))
24 
25 #include "xlockmore.h"
26 #include "involute.h"
27 #include "normals.h"
28 #include "tube.h"
29 #include "rotator.h"
30 #include "gltrackball.h"
31 #include <ctype.h>
32 
33 #ifdef USE_GL /* whole file */
34 
35 #undef BELLRAND
36 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
37 
38 #define DEF_SPIN        "True"
39 #define DEF_WANDER      "True"
40 #define DEF_SPEED       "1.0"
41 
42 typedef struct {
43   GLXContext *glx_context;
44   rotator *rot;
45   trackball_state *trackball;
46   Bool button_down_p;
47   Bool planetary_p;
48 
49   int ngears;
50   gear **gears;
51 
52   GLuint armature_dlist;
53   int armature_polygons;
54 
55   struct { GLfloat x1, y1, x2, y2; } bbox;
56 
57 } gears_configuration;
58 
59 static gears_configuration *bps = NULL;
60 
61 static Bool do_spin;
62 static GLfloat speed;
63 static Bool do_wander;
64 
65 static XrmOptionDescRec opts[] = {
66   { "-spin",   ".spin",   XrmoptionNoArg, "True"  },
67   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
68   { "-speed",  ".speed",  XrmoptionSepArg, 0      },
69   { "-wander", ".wander", XrmoptionNoArg, "True"  },
70   { "+wander", ".wander", XrmoptionNoArg, "False" },
71 };
72 
73 static argtype vars[] = {
74   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
75   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
76   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
77 };
78 
79 ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL};
80 
81 
82 /* Window management, etc
83  */
84 ENTRYPOINT void
reshape_gears(ModeInfo * mi,int width,int height)85 reshape_gears (ModeInfo *mi, int width, int height)
86 {
87   GLfloat h = (GLfloat) height / (GLfloat) width;
88   int y = 0;
89 
90   if (width > height * 5) {   /* tiny window: show middle */
91     height = width * 9/16;
92     y = -height/2;
93     h = height / (GLfloat) width;
94   }
95 
96   glViewport (0, y, (GLint) width, (GLint) height);
97 
98   glMatrixMode(GL_PROJECTION);
99   glLoadIdentity();
100   gluPerspective (30.0, 1/h, 1.0, 100.0);
101 
102   glMatrixMode(GL_MODELVIEW);
103   glLoadIdentity();
104   gluLookAt( 0.0, 0.0, 30.0,
105              0.0, 0.0, 0.0,
106              0.0, 1.0, 0.0);
107 
108 # ifdef HAVE_MOBILE	/* Keep it the same relative size when rotated. */
109   {
110     int o = (int) current_device_rotation();
111     if (o != 0 && o != 180 && o != -180)
112       glScalef (1/h, 1/h, 1/h);
113   }
114 # endif
115 
116   glClear(GL_COLOR_BUFFER_BIT);
117 }
118 
119 
120 static void
free_gear(gear * g)121 free_gear (gear *g)
122 {
123   if (g->dlist)
124     glDeleteLists (g->dlist, 1);
125   free (g);
126 }
127 
128 
129 /* Create and return a new gear sized for placement next to or on top of
130    the given parent gear (if any.)  Returns 0 if out of memory.
131    [Mostly lifted from pinion.c]
132  */
133 static gear *
new_gear(ModeInfo * mi,gear * parent)134 new_gear (ModeInfo *mi, gear *parent)
135 {
136   gears_configuration *bp = &bps[MI_SCREEN(mi)];
137   gear *g = (gear *) calloc (1, sizeof (*g));
138   static unsigned long id = 0;  /* only used in debugging output */
139 
140   if (!g) return 0;
141   g->id = ++id;
142 
143   /* Pick the size of the teeth.
144    */
145   if (parent) /* adjascent gears need matching teeth */
146     {
147       g->tooth_w = parent->tooth_w;
148       g->tooth_h = parent->tooth_h;
149       g->tooth_slope = -parent->tooth_slope;
150     }
151   else                 /* gears that begin trains get any size they want */
152     {
153       g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0));
154       g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0));
155 /*
156       g->tooth_slope = ((random() % 8)
157                         ? 0
158                         : 0.5 + BELLRAND(1));
159  */
160     }
161 
162   /* Pick the number of teeth, and thus, the radius.
163    */
164   {
165     double c;
166 
167     if (!parent || bp->ngears > 4)
168       g->nteeth = 5 + BELLRAND (20);
169     else
170       g->nteeth = parent->nteeth * (0.5 + BELLRAND(2));
171 
172     c = g->nteeth * g->tooth_w * 2;     /* circumference = teeth + gaps */
173     g->r = c / (M_PI * 2);              /* c = 2 pi r  */
174   }
175 
176   g->thickness  = g->tooth_w + frand (g->r);
177   g->thickness2 = g->thickness * 0.7;
178   g->thickness3 = g->thickness;
179 
180   /* Colorize
181    */
182   g->color[0] = 0.5 + frand(0.5);
183   g->color[1] = 0.5 + frand(0.5);
184   g->color[2] = 0.5 + frand(0.5);
185   g->color[3] = 1.0;
186 
187   g->color2[0] = g->color[0] * 0.85;
188   g->color2[1] = g->color[1] * 0.85;
189   g->color2[2] = g->color[2] * 0.85;
190   g->color2[3] = g->color[3];
191 
192 
193   /* Decide on shape of gear interior:
194      - just a ring with teeth;
195      - that, plus a thinner in-set "plate" in the middle;
196      - that, plus a thin raised "lip" on the inner plate;
197      - or, a wide lip (really, a thicker third inner plate.)
198    */
199   if ((random() % 10) == 0)
200     {
201       /* inner_r can go all the way in; there's no inset disc. */
202       g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
203       g->inner_r2 = 0;
204       g->inner_r3 = 0;
205     }
206   else
207     {
208       /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
209       g->inner_r  = (g->r * 0.5)  + frand((g->r - g->tooth_h) * 0.4);
210       g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
211       g->inner_r3 = 0;
212 
213       if (g->inner_r2 > (g->r * 0.2))
214         {
215           int nn = (random() % 10);
216           if (nn <= 2)
217             g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
218           else if (nn <= 7 && g->inner_r2 >= 0.1)
219             g->inner_r3 = g->inner_r2 - 0.01;
220         }
221     }
222 
223   /* If we have three discs, sometimes make the middle disc be spokes.
224    */
225   if (g->inner_r3 && ((random() % 5) == 0))
226     {
227       g->spokes = 2 + BELLRAND (5);
228       g->spoke_thickness = 1 + frand(7.0);
229       if (g->spokes == 2 && g->spoke_thickness < 2)
230         g->spoke_thickness += 1;
231     }
232 
233   /* Sometimes add little nubbly bits, if there is room.
234    */
235   if (g->nteeth > 5)
236     {
237       double size = 0;
238       involute_biggest_ring (g, 0, &size, 0);
239       if (size > g->r * 0.2 && (random() % 5) == 0)
240         {
241           g->nubs = 1 + (random() % 16);
242           if (g->nubs > 8) g->nubs = 1;
243         }
244     }
245 
246   if (g->inner_r3 > g->inner_r2) abort();
247   if (g->inner_r2 > g->inner_r) abort();
248   if (g->inner_r  > g->r) abort();
249 
250   /* Decide how complex the polygon model should be.
251    */
252   {
253     double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
254     if (pix <= 2.5)      g->size = INVOLUTE_SMALL;
255     else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
256     else if (pix <= 25)  g->size = INVOLUTE_LARGE;
257     else                 g->size = INVOLUTE_HUGE;
258   }
259 
260   g->base_p = !parent;
261 
262   return g;
263 }
264 
265 
266 /* Given a newly-created gear, place it next to its parent in the scene,
267    with its teeth meshed and the proper velocity.  Returns False if it
268    didn't work.  (Call this a bunch of times until either it works, or
269    you decide it's probably not going to.)
270    [Mostly lifted from pinion.c]
271  */
272 static Bool
place_gear(ModeInfo * mi,gear * g,gear * parent)273 place_gear (ModeInfo *mi, gear *g, gear *parent)
274 {
275   gears_configuration *bp = &bps[MI_SCREEN(mi)];
276 
277   /* Compute this gear's velocity.
278    */
279   if (! parent)
280     {
281       g->ratio = 0.8 + BELLRAND(0.4);  /* 0.8-1.2 = 8-12rpm @ 60fps */
282       g->th = 1; /* not 0 */
283     }
284   else
285     {
286       /* Gearing ratio is the ratio of the number of teeth to previous gear
287          (which is also the ratio of the circumferences.)
288        */
289       g->ratio = (double) parent->nteeth / (double) g->nteeth;
290 
291       /* Set our initial rotation to match that of the previous gear,
292          multiplied by the gearing ratio.  (This is finessed later,
293          once we know the exact position of the gear relative to its
294          parent.)
295       */
296       g->th = -(parent->th * g->ratio);
297 
298       if (g->nteeth & 1)    /* rotate 1/2 tooth-size if odd number of teeth */
299         {
300           double off = (180.0 / g->nteeth);
301           if (g->th > 0)
302             g->th += off;
303           else
304             g->th -= off;
305         }
306 
307       /* ratios are cumulative for all gears in the train. */
308       g->ratio *= parent->ratio;
309     }
310 
311 
312   if (parent)	/* Place the gear next to the parent. */
313     {
314       double r_off = parent->r + g->r;
315       int angle;
316 
317       angle = (random() % 360) - 180;   /* -180 to +180 degrees */
318 
319       g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
320       g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
321       g->z = parent->z;
322 
323       /* avoid accidentally changing sign of "th" in the math below. */
324       g->th += (g->th > 0 ? 360 : -360);
325 
326       /* Adjust the rotation of the gear so that its teeth line up with its
327          parent, based on the position of the gear and the current rotation
328          of the parent.
329        */
330       {
331         double p_c = 2 * M_PI * parent->r;  /* circumference of parent */
332         double g_c = 2 * M_PI * g->r;       /* circumference of g  */
333 
334         double p_t = p_c * (angle/360.0);   /* distance travelled along
335                                                circumference of parent when
336                                                moving "angle" degrees along
337                                                parent. */
338         double g_rat = p_t / g_c;           /* if travelling that distance
339                                                along circumference of g,
340                                                ratio of g's circumference
341                                                travelled. */
342         double g_th = 360.0 * g_rat;        /* that ratio in degrees */
343 
344         g->th += angle + g_th;
345       }
346     }
347 
348   /* If the position we picked for this gear causes it to overlap
349      with any earlier gear in the train, give up.
350    */
351   {
352     int i;
353 
354     for (i = bp->ngears-1; i >= 0; i--)
355       {
356         gear *og = bp->gears[i];
357 
358         if (og == g) continue;
359         if (og == parent) continue;
360         if (g->z != og->z) continue;    /* Ignore unless on same layer */
361 
362         /* Collision detection without sqrt:
363              d = sqrt(a^2 + b^2)   d^2 = a^2 + b^2
364              d < r1 + r2           d^2 < (r1 + r2)^2
365          */
366         if (((g->x - og->x) * (g->x - og->x) +
367              (g->y - og->y) * (g->y - og->y)) <
368             ((g->r + g->tooth_h + og->r + og->tooth_h) *
369              (g->r + g->tooth_h + og->r + og->tooth_h)))
370           return False;
371       }
372   }
373 
374   return True;
375 }
376 
377 
378 /* Make a new gear, place it next to its parent in the scene,
379    with its teeth meshed and the proper velocity.  Returns the gear;
380    or 0 if it didn't work.  (Call this a bunch of times until either
381    it works, or you decide it's probably not going to.)
382    [Mostly lifted from pinion.c]
383  */
384 static gear *
place_new_gear(ModeInfo * mi,gear * parent)385 place_new_gear (ModeInfo *mi, gear *parent)
386 {
387   gears_configuration *bp = &bps[MI_SCREEN(mi)];
388   int loop_count = 0;
389   gear *g = 0;
390 
391   while (1)
392     {
393       loop_count++;
394       if (loop_count >= 100)
395         {
396           if (g)
397             free_gear (g);
398           g = 0;
399           break;
400         }
401 
402       g = new_gear (mi, parent);
403       if (!g) return 0;  /* out of memory? */
404 
405       if (place_gear (mi, g, parent))
406         break;
407     }
408 
409   if (! g) return 0;
410 
411   /* We got a gear, and it is properly positioned.
412      Insert it in the scene.
413    */
414   bp->gears[bp->ngears++] = g;
415   return g;
416 }
417 
418 
419 static int
arm(GLfloat length,GLfloat width1,GLfloat height1,GLfloat width2,GLfloat height2,Bool wire)420 arm (GLfloat length,
421      GLfloat width1, GLfloat height1,
422      GLfloat width2, GLfloat height2,
423      Bool wire)
424 {
425   int polys = 0;
426   glShadeModel(GL_FLAT);
427 
428 #if 0  /* don't need these - they're embedded in other objects */
429   /* draw end 1 */
430   glFrontFace(GL_CW);
431   glNormal3f(-1, 0, 0);
432   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
433   glVertex3f(-length/2, -width1/2, -height1/2);
434   glVertex3f(-length/2,  width1/2, -height1/2);
435   glVertex3f(-length/2,  width1/2,  height1/2);
436   glVertex3f(-length/2, -width1/2,  height1/2);
437   polys++;
438   glEnd();
439 
440   /* draw end 2 */
441   glFrontFace(GL_CCW);
442   glNormal3f(1, 0, 0);
443   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
444   glVertex3f(length/2, -width2/2, -height2/2);
445   glVertex3f(length/2,  width2/2, -height2/2);
446   glVertex3f(length/2,  width2/2,  height2/2);
447   glVertex3f(length/2, -width2/2,  height2/2);
448   polys++;
449   glEnd();
450 #endif
451 
452   /* draw top */
453   glFrontFace(GL_CCW);
454   glNormal3f(0, 0, -1);
455   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
456   glVertex3f(-length/2, -width1/2, -height1/2);
457   glVertex3f(-length/2,  width1/2, -height1/2);
458   glVertex3f( length/2,  width2/2, -height2/2);
459   glVertex3f( length/2, -width2/2, -height2/2);
460   polys++;
461   glEnd();
462 
463   /* draw bottom */
464   glFrontFace(GL_CW);
465   glNormal3f(0, 0, 1);
466   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
467   glVertex3f(-length/2, -width1/2, height1/2);
468   glVertex3f(-length/2,  width1/2, height1/2);
469   glVertex3f( length/2,  width2/2, height2/2);
470   glVertex3f( length/2, -width2/2, height2/2);
471   polys++;
472   glEnd();
473 
474   /* draw left */
475   glFrontFace(GL_CW);
476   glNormal3f(0, -1, 0);
477   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
478   glVertex3f(-length/2, -width1/2, -height1/2);
479   glVertex3f(-length/2, -width1/2,  height1/2);
480   glVertex3f( length/2, -width2/2,  height2/2);
481   glVertex3f( length/2, -width2/2, -height2/2);
482   polys++;
483   glEnd();
484 
485   /* draw right */
486   glFrontFace(GL_CCW);
487   glNormal3f(0, 1, 0);
488   glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
489   glVertex3f(-length/2,  width1/2, -height1/2);
490   glVertex3f(-length/2,  width1/2,  height1/2);
491   glVertex3f( length/2,  width2/2,  height2/2);
492   glVertex3f( length/2,  width2/2, -height2/2);
493   polys++;
494   glEnd();
495 
496   glFrontFace(GL_CCW);
497 
498   return polys;
499 }
500 
501 
502 static int
ctube(GLfloat diameter,GLfloat width,Bool wire)503 ctube (GLfloat diameter, GLfloat width, Bool wire)
504 {
505   tube (0, 0,  width/2,
506         0, 0, -width/2,
507         diameter, 0,
508         32, True, True, wire);
509   return 0; /* #### */
510 }
511 
512 static void
armature(ModeInfo * mi)513 armature (ModeInfo *mi)
514 {
515   gears_configuration *bp = &bps[MI_SCREEN(mi)];
516   int wire = MI_IS_WIREFRAME(mi);
517 
518   static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
519   GLfloat shiny = 128.0;
520   GLfloat color[4];
521 
522   color[0] = 0.5 + frand(0.5);
523   color[1] = 0.5 + frand(0.5);
524   color[2] = 0.5 + frand(0.5);
525   color[3] = 1.0;
526 
527   bp->armature_polygons = 0;
528 
529   bp->armature_dlist = glGenLists (1);
530   if (! bp->armature_dlist)
531     {
532       check_gl_error ("glGenLists");
533       abort();
534     }
535 
536   glNewList (bp->armature_dlist, GL_COMPILE);
537 
538   glMaterialfv (GL_FRONT, GL_SPECULAR,  spec);
539   glMateriali  (GL_FRONT, GL_SHININESS, shiny);
540   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
541   glColor3f (color[0], color[1], color[2]);
542 
543   glPushMatrix();
544 
545   {
546     GLfloat s = bp->gears[0]->r * 2.7;
547     s = s/5.6;
548     glScalef (s, s, s);
549   }
550 
551   glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness);
552   glRotatef (30, 0, 0, 1);
553 
554   bp->armature_polygons += ctube (0.5, 10, wire);       /* center axle */
555 
556   glPushMatrix();
557   glTranslatef(0.0, 4.2, -1);
558   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 1 */
559   glTranslatef(0, 0, 1.8);
560   bp->armature_polygons += ctube (0.7, 0.7, wire);
561   glPopMatrix();
562 
563   glPushMatrix();
564   glRotatef(120, 0.0, 0.0, 1.0);
565   glTranslatef(0.0, 4.2, -1);
566   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 2 */
567   glTranslatef(0, 0, 1.8);
568   bp->armature_polygons += ctube (0.7, 0.7, wire);
569   glPopMatrix();
570 
571   glPushMatrix();
572   glRotatef(240, 0.0, 0.0, 1.0);
573   glTranslatef(0.0, 4.2, -1);
574   bp->armature_polygons += ctube (0.5, 3, wire);       /* axle 3 */
575   glTranslatef(0, 0, 1.8);
576   bp->armature_polygons += ctube (0.7, 0.7, wire);
577   glPopMatrix();
578 
579   glTranslatef(0, 0, 1.5);			      /* center disk */
580   bp->armature_polygons += ctube (1.5, 2, wire);
581 
582   glPushMatrix();
583   glRotatef(270, 0, 0, 1);
584   glRotatef(-10, 0, 1, 0);
585   glTranslatef(-2.2, 0, 0);
586   bp->armature_polygons += arm (4.0, 1.0, 0.5,
587                                 2.0, 1.0, wire);	/* arm 1 */
588   glPopMatrix();
589 
590   glPushMatrix();
591   glRotatef(30, 0, 0, 1);
592   glRotatef(-10, 0, 1, 0);
593   glTranslatef(-2.2, 0, 0);
594   bp->armature_polygons += arm (4.0, 1.0, 0.5,
595                                 2.0, 1.0, wire);	/* arm 2 */
596   glPopMatrix();
597 
598   glPushMatrix();
599   glRotatef(150, 0, 0, 1);
600   glRotatef(-10, 0, 1, 0);
601   glTranslatef(-2.2, 0, 0);
602   bp->armature_polygons += arm (4.0, 1.0, 0.5,
603                                 2.0, 1.0, wire);	/* arm 3 */
604   glPopMatrix();
605 
606   glPopMatrix();
607 
608   glEndList ();
609 }
610 
611 
612 static void
planetary_gears(ModeInfo * mi)613 planetary_gears (ModeInfo *mi)
614 {
615   gears_configuration *bp = &bps[MI_SCREEN(mi)];
616   gear *g0, *g1, *g2, *g3, *g4;
617   GLfloat distance = 2.02;
618 
619   bp->planetary_p = True;
620 
621   g0 = new_gear (mi, 0);
622   g1 = new_gear (mi, 0);
623   g2 = new_gear (mi, 0);
624   g3 = new_gear (mi, 0);
625   g4 = new_gear (mi, 0);
626 
627   if (! place_gear (mi, g0, 0)) abort();
628   if (! place_gear (mi, g1, 0)) abort();
629   if (! place_gear (mi, g2, 0)) abort();
630   if (! place_gear (mi, g3, 0)) abort();
631   if (! place_gear (mi, g4, 0)) abort();
632 
633   g0->nteeth = 12 + (3 * (random() % 10));  /* must be multiple of 3 */
634   g0->tooth_w = g0->r / g0->nteeth;
635   g0->tooth_h = g0->tooth_w * 2.8;
636 
637 # define COPY(F) g4->F = g3->F = g2->F = g1->F = g0->F
638   COPY(r);
639   COPY(th);
640   COPY(nteeth);
641   COPY(tooth_w);
642   COPY(tooth_h);
643   COPY(tooth_slope);
644   COPY(inner_r);
645   COPY(inner_r2);
646   COPY(inner_r3);
647   COPY(thickness);
648   COPY(thickness2);
649   COPY(thickness3);
650   COPY(ratio);
651   COPY(size);
652 # undef COPY
653 
654   g1->x = cos (M_PI * 2 / 3) * g1->r * distance;
655   g1->y = sin (M_PI * 2 / 3) * g1->r * distance;
656 
657   g2->x = cos (M_PI * 4 / 3) * g2->r * distance;
658   g2->y = sin (M_PI * 4 / 3) * g2->r * distance;
659 
660   g3->x = cos (M_PI * 6 / 3) * g3->r * distance;
661   g3->y = sin (M_PI * 6 / 3) * g3->r * distance;
662 
663   g4->x = 0;
664   g4->y = 0;
665   g4->th = -g3->th;
666 
667   /* rotate central gear 1/2 tooth-size if odd number of teeth */
668   if (g4->nteeth & 1)
669     g4->th -= (180.0 / g4->nteeth);
670 
671   g0->inverted_p  = True;
672   g0->x           = 0;
673   g0->y           = 0;
674   g0->nteeth      = g1->nteeth * 3;
675   g0->r           = g1->r * 3.05;
676   g0->inner_r     = g0->r * 0.8;
677   g0->inner_r2    = 0;
678   g0->inner_r3    = 0;
679   g0->th          = g1->th + (180 / g0->nteeth);
680   g0->ratio       = g1->ratio / 3;
681 
682   g0->tooth_slope = 0;
683   g0->nubs        = 3;
684   g0->spokes      = 0;
685   g0->size        = INVOLUTE_LARGE;
686 
687   bp->gears = (gear **) calloc (6, sizeof(*bp->gears));
688   bp->ngears = 0;
689 
690   bp->gears[bp->ngears++] = g1;
691   bp->gears[bp->ngears++] = g2;
692   bp->gears[bp->ngears++] = g3;
693   bp->gears[bp->ngears++] = g4;
694   bp->gears[bp->ngears++] = g0;
695 }
696 
697 
698 
699 
700 ENTRYPOINT void
init_gears(ModeInfo * mi)701 init_gears (ModeInfo *mi)
702 {
703   gears_configuration *bp;
704   int wire = MI_IS_WIREFRAME(mi);
705   int i;
706 
707   MI_INIT (mi, bps);
708 
709   bp = &bps[MI_SCREEN(mi)];
710 
711   bp->glx_context = init_GL(mi);
712 
713   reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
714 
715   if (!wire)
716     {
717       GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
718       GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
719       GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
720       GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
721 
722       glEnable(GL_LIGHTING);
723       glEnable(GL_LIGHT0);
724       glEnable(GL_DEPTH_TEST);
725       glEnable(GL_CULL_FACE);
726 
727       glLightfv(GL_LIGHT0, GL_POSITION, pos);
728       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
729       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
730       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
731     }
732 
733   if (! bp->rot)
734     {
735       double spin_speed   = 0.5;
736       double wander_speed = 0.01;
737       double spin_accel   = 0.25;
738 
739       bp->rot = make_rotator (do_spin ? spin_speed : 0,
740                               do_spin ? spin_speed : 0,
741                               do_spin ? spin_speed : 0,
742                               spin_accel,
743                               do_wander ? wander_speed : 0,
744                               True
745                               );
746       bp->trackball = gltrackball_init (True);
747     }
748 
749   if (bp->gears)
750     {
751       for (i = 0; i < bp->ngears; i++)
752         free_gear (bp->gears[i]);
753       free (bp->gears);
754       bp->gears = 0;
755       bp->ngears = 0;
756     }
757 
758   if (!(random() % 8))
759     {
760       planetary_gears (mi);
761     }
762   else
763     {
764       gear *g = 0;
765       int total_gears = MI_COUNT (mi);
766 
767       bp->planetary_p = False;
768 
769       if (total_gears <= 0)
770         total_gears = 3 + fabs (BELLRAND (8) - 4);  /* 3 - 7, mostly 3. */
771       bp->gears = (gear **) calloc (total_gears+2, sizeof(*bp->gears));
772       bp->ngears = 0;
773 
774       for (i = 0; i < total_gears; i++)
775         g = place_new_gear (mi, g);
776     }
777 
778 
779   /* Center gears in scene. */
780   {
781     GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999;
782     int i;
783     for (i = 0; i < bp->ngears; i++)
784       {
785         gear *g = bp->gears[i];
786         if (g->x - g->r < minx) minx = g->x - g->r;
787         if (g->x + g->r > maxx) maxx = g->x + g->r;
788         if (g->y - g->r < miny) miny = g->y - g->r;
789         if (g->y + g->r > maxy) maxy = g->y + g->r;
790       }
791     bp->bbox.x1 = minx;
792     bp->bbox.y1 = miny;
793     bp->bbox.x2 = maxx;
794     bp->bbox.y2 = maxy;
795   }
796 
797   /* Now render each gear into its display list.
798    */
799   for (i = 0; i < bp->ngears; i++)
800     {
801       gear *g = bp->gears[i];
802       g->dlist = glGenLists (1);
803       if (! g->dlist)
804         {
805           check_gl_error ("glGenLists");
806           abort();
807         }
808 
809       glNewList (g->dlist, GL_COMPILE);
810       g->polygons += draw_involute_gear (g, wire);
811       glEndList ();
812     }
813   if (bp->planetary_p)
814     armature (mi);
815 }
816 
817 
818 ENTRYPOINT void
draw_gears(ModeInfo * mi)819 draw_gears (ModeInfo *mi)
820 {
821   gears_configuration *bp = &bps[MI_SCREEN(mi)];
822   Display *dpy = MI_DISPLAY(mi);
823   Window window = MI_WINDOW(mi);
824   int i;
825 
826   if (!bp->glx_context)
827     return;
828 
829   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
830 
831   glShadeModel(GL_SMOOTH);
832 
833   glEnable(GL_DEPTH_TEST);
834   glEnable(GL_NORMALIZE);
835   glEnable(GL_CULL_FACE);
836 
837   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
838 
839   glPushMatrix ();
840 
841   {
842     double x, y, z;
843     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
844     glTranslatef ((x - 0.5) * 4,
845                   (y - 0.5) * 4,
846                   (z - 0.5) * 7);
847 
848     gltrackball_rotate (bp->trackball);
849 
850     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
851 
852     /* add a little rotation for -no-spin mode */
853     x -= 0.14;
854     y -= 0.06;
855 
856     glRotatef (x * 360, 1.0, 0.0, 0.0);
857     glRotatef (y * 360, 0.0, 1.0, 0.0);
858     glRotatef (z * 360, 0.0, 0.0, 1.0);
859   }
860 
861   /* Center the scene's bounding box in the window,
862      and scale it to fit.
863    */
864   {
865     GLfloat w = bp->bbox.x2 - bp->bbox.x1;
866     GLfloat h = bp->bbox.y2 - bp->bbox.y1;
867     GLfloat s = 10.0 / (w > h ? w : h);
868     glScalef (s, s, s);
869     glTranslatef (-(bp->bbox.x1 + w/2),
870                   -(bp->bbox.y1 + h/2),
871                   0);
872   }
873 
874   mi->polygon_count = 0;
875 
876   for (i = 0; i < bp->ngears; i++)
877     {
878       gear *g = bp->gears[i];
879 
880       glPushMatrix();
881 
882       glTranslatef (g->x, g->y, g->z);
883       glRotatef (g->th, 0, 0, 1);
884 
885       glCallList (g->dlist);
886       mi->polygon_count += g->polygons;
887 
888       glPopMatrix ();
889     }
890 
891   if (bp->planetary_p)
892     {
893       glCallList (bp->armature_dlist);
894       mi->polygon_count += bp->armature_polygons;
895     }
896 
897   glPopMatrix ();
898 
899   /* spin gears */
900   if (!bp->button_down_p)
901     for (i = 0; i < bp->ngears; i++)
902       {
903         gear *g = bp->gears[i];
904         double off = g->ratio * 5 * speed;
905         if (g->th > 0)
906           g->th += off;
907         else
908           g->th -= off;
909       }
910 
911   if (mi->fps_p) do_fps (mi);
912   glFinish();
913 
914   glXSwapBuffers(dpy, window);
915 }
916 
917 ENTRYPOINT Bool
gears_handle_event(ModeInfo * mi,XEvent * event)918 gears_handle_event (ModeInfo *mi, XEvent *event)
919 {
920   gears_configuration *bp = &bps[MI_SCREEN(mi)];
921 
922   if (gltrackball_event_handler (event, bp->trackball,
923                                  MI_WIDTH (mi), MI_HEIGHT (mi),
924                                  &bp->button_down_p))
925     return True;
926   else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
927     {
928       init_gears (mi);
929       return True;
930     }
931 
932   return False;
933 }
934 
935 
936 ENTRYPOINT void
free_gears(ModeInfo * mi)937 free_gears (ModeInfo *mi)
938 {
939   gears_configuration *bp = &bps[MI_SCREEN(mi)];
940   int i;
941   if (!bp->glx_context) return;
942   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context);
943   if (bp->rot) free_rotator (bp->rot);
944   if (bp->trackball) gltrackball_free (bp->trackball);
945   for (i = 0; i < bp->ngears; i++)
946     if (bp->gears[i])
947       free_gear (bp->gears[i]);
948   if (glIsList(bp->armature_dlist))
949     glDeleteLists (bp->armature_dlist, 1);
950 
951 }
952 
953 XSCREENSAVER_MODULE ("Gears", gears)
954 
955 #endif /* USE_GL */
956