Making a demo program that uses "glutMainLoopEvent"
[freeglut] / progs / demos / Fractals / fractals.c
1 /* fractals.c */
2 /*
3  * Program to draw a fractal by Michael Barnsley's deterministic algorithm.
4  * Algorithm:
5  *  (1) Define the affine transformations (of the form r(i+1) = A r(i) + b )
6  *  (2) Find the stationary point for each transformation
7  *  (3) To draw:
8  *        - If you are at the lowest level, draw lines connecting all the stationary points
9  *        - If not, call the draw function recursively with each affine transformation applied
10  */
11
12 /*
13  * User Commands:
14  *  +,- - increment/decrement number of levels
15  *  PgUp, PgDn - increase/decrease scaling
16  *  Arrow keys - translate viewing section
17  *  r - reset view
18  *  Escape - quit
19  */
20
21 #include <GL/freeglut.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26
27 typedef struct
28 {
29   double a00, a01, a10, a11 ;   /* Transformation matrix */
30   double b0, b1 ;               /* Constant vector added on */
31   double statx, staty ;         /* Coordinates of the stationary point */
32 }
33 AffineTrans ;
34
35 /* Number of levels to draw the fractal */
36 static int num_levels = 0 ;
37
38 /* The definition of the fractal */
39 static int num_trans ;
40 static AffineTrans *affine ;
41
42 /* Flag telling us to keep executing the main loop */
43 static int continue_in_main_loop = 1;
44
45 /* the window title */
46 char window_title [ 80 ] ;
47
48 /* The amount the view is translated and scaled */
49 double xwin = 0.0, ywin = 0.0 ;
50 double scale_factor = 1.0 ;
51
52 static void draw_level ( int num, double m00, double m01, double m10, double m11, double n0, double n1 )
53 {
54   /* Draw a fractal transformed by "M", "N" as passed in */
55   int i ;
56
57   if ( num == 0 )
58   {
59     double x0 = m00 * affine[0].statx + m01 * affine[0].staty + n0 ;
60     double y0 = m10 * affine[0].statx + m11 * affine[0].staty + n1 ;
61
62     for ( i = 1; i < num_trans; i++ )
63     {
64       double x1 = m00 * affine[i].statx + m01 * affine[i].staty + n0 ;
65       double y1 = m10 * affine[i].statx + m11 * affine[i].staty + n1 ;
66
67       glVertex2d ( x0, y0 ) ;
68       glVertex2d ( x1, y1 ) ;
69
70       x0 = x1 ;
71       y0 = y1 ;
72     }
73   }
74   else
75   {
76     /* Map each affine transformation in the fractal through the one passed in and call "draw_level" */
77
78     for ( i = 0; i < num_trans; i++ )
79     {
80       draw_level ( num-1, m00*affine[i].a00+m01*affine[i].a10,     m00*affine[i].a01+m01*affine[i].a11,
81                           m10*affine[i].a00+m11*affine[i].a10,     m10*affine[i].a01+m11*affine[i].a11,
82                           m00*affine[i].b0 +m01*affine[i].b1 + n0, m10*affine[i].b0 +m11*affine[i].b1  + n1 ) ;
83     }
84   }
85 }
86
87 static void 
88 Display(void)
89 {
90   glClear( GL_COLOR_BUFFER_BIT );
91
92   /* the curve */
93   glPushMatrix();
94   glScalef(2.5, 2.5, 2.5);
95
96   glColor4f(0.0, 0.0, 0.0, 1.0);
97   glBegin ( GL_LINES ) ;
98   draw_level ( num_levels, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 );
99   glEnd () ;
100
101   glPopMatrix();
102   glutSwapBuffers();
103 }
104
105 static void 
106 Reshape(int width, int height)
107 {
108   float ar;
109   glViewport ( 0, 0, width, height ) ;
110   glMatrixMode ( GL_PROJECTION ) ;
111   glLoadIdentity();
112   ar = (float) width / (float) height ;
113   if( ar > 1 )
114       glFrustum ( -ar, ar, -1.0, 1.0, 2.0, 100.0 ) ;
115   else
116       glFrustum ( -1.0, 1.0, -1/ar, 1/ar, 2.0, 100.0 );
117   glMatrixMode ( GL_MODELVIEW ) ;
118   glLoadIdentity () ;
119   xwin = -1.0 ;
120   ywin =  0.0 ;
121   glTranslated ( xwin, ywin, -5.0 ) ;
122 }
123
124 static void 
125 Key(unsigned char key, int x, int y)
126 {
127   int need_redisplay = 1;
128   
129   switch (key) {
130   case 27:  /* Escape key */
131     continue_in_main_loop = 0 ;
132     break;
133
134   case '+' :
135     ++num_levels ;
136     break ;
137
138   case '-' :
139     if ( num_levels > 0 )
140       --num_levels ;
141     break ;
142
143   case 'r' :  case 'R' :
144     glMatrixMode ( GL_MODELVIEW ) ;
145     glLoadIdentity();
146     xwin = -1.0 ;
147     ywin = 0.0 ;
148     glTranslated ( xwin, ywin, -5.0 ) ;
149     break ;
150
151   default:
152     need_redisplay = 0;
153     break;
154   }
155   if (need_redisplay)
156     glutPostRedisplay();
157 }
158
159 static void 
160 Special(int key, int x, int y)
161 {
162   int need_redisplay = 1;
163
164   switch (key) {
165   case GLUT_KEY_UP :
166     glMatrixMode ( GL_MODELVIEW ) ;
167     ywin += 0.1 * scale_factor ;
168     glTranslated ( 0.0, 0.1 * scale_factor, 0.0 ) ;
169     break ;
170
171   case GLUT_KEY_DOWN :
172     glMatrixMode ( GL_MODELVIEW ) ;
173     ywin -= 0.1 * scale_factor ;
174     glTranslated ( 0.0, -0.1 * scale_factor, 0.0 ) ;
175     break ;
176
177   case GLUT_KEY_LEFT :
178     glMatrixMode ( GL_MODELVIEW ) ;
179     xwin -= 0.1 * scale_factor ;
180     glTranslated ( -0.1 * scale_factor, 0.0, 0.0 ) ;
181     break ;
182
183   case GLUT_KEY_RIGHT :
184     glMatrixMode ( GL_MODELVIEW ) ;
185     xwin += 0.1 * scale_factor ;
186     glTranslated ( 0.1 * scale_factor, 0.0, 0.0 ) ;
187     break ;
188
189   case GLUT_KEY_PAGE_UP :
190     glMatrixMode ( GL_MODELVIEW ) ;
191     glTranslated ( -xwin, -ywin, 0.0 ) ;
192     glScaled ( 1.25, 1.25, 1.25 ) ;
193     glTranslated ( xwin, ywin, 0.0 ) ;
194     scale_factor *= 0.8 ;
195     break ;
196
197   case GLUT_KEY_PAGE_DOWN :
198     glMatrixMode ( GL_MODELVIEW ) ;
199     glTranslated ( -xwin, -ywin, 0.0 ) ;
200     glScaled ( 0.8, 0.8, 0.8 ) ;
201     glTranslated ( xwin, ywin, 0.0 ) ;
202     scale_factor *= 1.25 ;
203     break ;
204
205   default:
206     need_redisplay = 0;
207     break;
208   }
209   if (need_redisplay)
210     glutPostRedisplay();
211 }
212
213
214 static void
215 checkedFGets ( char *s, int size, FILE *stream )
216 {
217   if ( fgets ( s, size, stream ) == NULL ) {
218     fprintf ( stderr, "fgets failed\n");
219     exit ( EXIT_FAILURE );
220   }
221 }
222
223
224 void readConfigFile ( char *fnme )
225 {
226   FILE *fptr = fopen ( fnme, "rt" ) ;
227   int i ;
228   char inputline [ 256 ] ;
229
230   if ( fptr )
231   {
232     /* Read a header line */
233     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
234
235     /* Read a comment line */
236     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
237
238     /* Read the window title */
239     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
240     /* We assume here that this line will not exceed 79 characters plus a 
241        newline (window_title is 80 characters long). That'll cause a buffer 
242        overflow. For a simple program like  this, though, we're letting it 
243        slide! 
244     */
245     sscanf ( inputline, "%[a-zA-Z0-9!@#$%^&*()+=/\\_-\" ]", window_title ) ; 
246
247     /* Read a comment line */
248     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
249
250     /* Read the number of affine transformations */
251     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
252     sscanf ( inputline, "%d", &num_trans ) ;
253
254     affine = (AffineTrans *)malloc ( num_trans * sizeof(AffineTrans) ) ;
255
256     /* Read a comment line */
257     checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
258
259     for ( i = 0; i < num_trans; i++ )
260     {
261       /* Read an affine transformation definition */
262       checkedFGets ( inputline, sizeof ( inputline ), fptr ) ;
263       sscanf ( inputline, "%lf %lf %lf %lf %lf %lf", &affine[i].a00, &affine[i].a01,
264                        &affine[i].a10, &affine[i].a11, &affine[i].b0, &affine[i].b1 ) ;
265     }
266   }
267   else  /* No data file, set a default */
268   {
269     printf ( "ERROR opening file <%s>\n", fnme ) ;
270     strcpy ( window_title, "Cantor Dust" ) ;
271     num_trans = 2 ;
272     affine = (AffineTrans *)malloc ( num_trans * sizeof(AffineTrans) ) ;
273     affine[0].a00 = 0.25 ;  affine[0].a01 = 0.00 ;  affine[0].a10 = 0.00 ;  affine[0].a11 = 0.25 ;
274     affine[0].b0 = 0.0 ;    affine[0].b1 = 0.0 ;
275     affine[1].a00 = 0.25 ;  affine[1].a01 = 0.00 ;  affine[1].a10 = 0.00 ;  affine[1].a11 = 0.25 ;
276     affine[1].b0 = 0.5 ;    affine[1].b1 = 0.0 ;
277   }
278
279   for ( i = 0; i < num_trans; i++ )
280   {
281     double m00, m01, m10, m11 ;  /* Matrix "I" minus "A" */
282     double determ ;              /* Determinant of this matrix */
283
284     /* Calculate the stationary point */
285
286     m00 = 1.0 - affine[i].a00 ;
287     m01 =     - affine[i].a01 ;
288     m10 =     - affine[i].a10 ;
289     m11 = 1.0 - affine[i].a11 ;
290
291     determ = m00 * m11 - m01 * m10 ;
292
293     if ( fabs ( determ ) > 1.e-6 )
294     {
295       affine[i].statx = (  m11 * affine[i].b0 - m01 * affine[i].b1 ) / determ ;
296       affine[i].staty = ( -m10 * affine[i].b0 + m00 * affine[i].b1 ) / determ ;
297     }
298     else
299       affine[i].statx = affine[i].staty = 0.0 ;
300   }
301 }
302
303 int 
304 main(int argc, char *argv[])
305 {
306   int fractal_window ;
307
308   glutInitWindowSize(500, 250);
309   glutInitWindowPosition ( 140, 140 );
310   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE );
311   glutInit(&argc, argv);
312
313   if ( argc > 1 )
314     readConfigFile ( argv[1] ) ;
315   else
316     readConfigFile ( "fractals.dat" ) ;
317
318   fractal_window = glutCreateWindow( window_title );
319
320   glClearColor(1.0, 1.0, 1.0, 1.0);
321
322   glutReshapeFunc(Reshape);
323   glutKeyboardFunc(Key);
324   glutSpecialFunc(Special);
325   glutDisplayFunc(Display);
326
327 #ifdef WIN32
328 #endif
329
330   while ( continue_in_main_loop )
331     glutMainLoopEvent();
332
333   printf ( "Back from the 'freeglut' main loop\n" ) ;
334
335   return 0;             /* ANSI C requires main to return int. */
336 }