1:  #include <GL/glut.h>
2:  #include <stdlib.h>
3:  #include <stdio.h>
4:  #include <string.h>
5:  #include <math.h>
6:  
7:  #define Round(v) ((int)(v+0.5))
8:  #define MAX 800
9:  #define MAX_FILE_LENGTH 256
10:    
11:  #define LOW_VALUE 0 
12:  #define HIGH_VALUE 255
13:  
14:  #define TRUE 1
15:  #define FALSE 0  
16:  
17:  /*cartesian coordinate type*/
18:  typedef struct {int x;
19:                  int y;}coord;  
20:  
21:  /*RGB color struct with floats*/ 
22:  /*typedef struct {float red;
23:                  float green;
24:                  float blue;
25:                 }RGB;
26:  */
27:  /*RGB color struct with ints*/  
28:  typedef struct {int red;
29:                  int green;
30:                  int blue;
31:                 }RGB_INT;
32:  
33:  /*number of chain codes stored, */  
34:  #define MAX_NUMBER_OF_CHAINS 10
35:  #define MAX_CHAINS MAX_NUMBER_OF_CHAINS
36:  
37:  /*These constant values are the chain code directions*/
38:  #define NONE      -1
39:  #define EAST      0
40:  #define NORTHEAST 1
41:  #define NORTH     2
42:  #define NORTHWEST 3
43:  #define WEST      4  
44:  #define SOUTHWEST 5
45:  #define SOUTH     6
46:  #define SOUTHEAST 7
47:  
48:  typedef struct chainCode* chain_t;
49:  typedef struct chainCode
50:  {
51:    chain_t prev;
52:    int code;    
53:    coord location; /*absolute pixel location for starting point*/
54:    chain_t next;
55:  }chainCode; /*This struct can be refered to by: struct chainCode or chainCode*/
56:  
57:  struct PGMstructure 
58:  {
59:    int maxVal;
60:    int width;
61:    int height;
62:    RGB_INT data[MAX][MAX];
63:  };
64:  
65:  typedef struct PGMstructure PGMImage;
66:  
67:  /*Evil globals, but not do-able otherwise.*/
68:  static PGMImage *img_cur;
69:  static PGMImage *img0; /*original*/
70:  static PGMImage *img1; /*current*/
71:  static PGMImage *img2; /*current*/
72:  static int HSIZE;
73:  static int VSIZE;
74:  static int MVAL;
75:  
76:  const RGB_INT white = {255, 255, 255};
77:  const RGB_INT yellow = {255, 255, 0};
78:  const RGB_INT purple = {255, 0, 255};
79:  const RGB_INT cyan = {0, 255, 255};
80:  const RGB_INT red = {255, 0, 0};
81:  const RGB_INT green = {0, 255, 0};
82:  const RGB_INT blue = {0, 0, 255};
83:  const RGB_INT black = {0, 0, 0};
84:  
85:  int cur_threash_val; /*for speed optimization of chain code recursion*/
86:  int cur_chain_count, back_ground;
87:  
88:  /**************Drawing funcitions************************************/
89:  /********************************************************************/
90:  
91:  void setCPixel(int ix, int iy, RGB_INT color)
92:  /*Same as setIPixel except that the last parameter is an RGB color*/
93:  {
94:    float x = (ix*2.0)/HSIZE - 1.0;
95:    float y = (iy*2.0)/VSIZE - 1.0;
96:  
97:    float red = (float)color.red/(float)MVAL;
98:    float green = (float)color.green/(float)MVAL;
99:    float blue = (float)color.blue/(float)MVAL;
100:  
101:    glColor3f(red, green, blue);
102:    
103:    glBegin(GL_POINTS); 
104:       glVertex2f (x, y);
105:    glEnd();
106:  }
107:  
108:  void setCLines(int ix1, int iy1, int ix2, int iy2, RGB_INT color)
109:  /*Similar as setIPixel except that this one draws a line between the first set
110:    of points given and the second set in the RGB color specified*/
111:  {
112:    float x1 = (ix1*2.0)/HSIZE - 1.0;
113:    float y1 = (iy1*2.0)/VSIZE - 1.0;
114:    float x2 = (ix2*2.0)/HSIZE - 1.0;
115:    float y2 = (iy2*2.0)/VSIZE -~ 1.0;
116:   
117:    float red = (float)color.red/(float)MVAL;
118:    float green = (float)color.green/(float)MVAL;
119:    float blue = (float)color.blue/(float)MVAL;
120:  
121:    glColor3f(red, green, blue);
122:  
123:    glBegin(GL_LINES);
124:      glVertex2f (x1, y1);
125:      glVertex2f (x2, y2);
126:    glEnd();
127:  }
128:  
129:  void setCRect(int ix1, int iy1, int ix2, int iy2, RGB_INT color)
130:  /*Similar as setIPixel except that this one draws a line between the first set
131:    of points given and the second set in the RGB color specified*/
132:  {
133:    float x1 = (ix1*2.0)/HSIZE - 1.0;
134:    float y1 = (iy1*2.0)/VSIZE - 1.0;
135:    float x2 = (ix2*2.0)/HSIZE - 1.0;
136:    float y2 = (iy2*2.0)/VSIZE - 1.0;
137:   
138:    float red = (float)color.red/(float)MVAL;
139:    float green = (float)color.green/(float)MVAL;
140:    float blue = (float)color.blue/(float)MVAL;
141:  
142:    glColor3f(red, green, blue);
143:  
144:    glBegin(GL_POLYGON);
145:      glVertex2f (x1, y1);
146:      glVertex2f (x1, y2);
147:      glVertex2f (x2, y2);
148:      glVertex2f (x2, y1);
149:    glEnd();
150:  }
151:  
152:  void pxlcpy(PGMImage *dest, int dest_row, int dest_col,
153:  	    PGMImage *src, int src_row, int src_col)
154:  {
155:    /*make sure values are within bounds*/
156:    if(dest_col > 0 && dest_col < (*dest).width
157:       && dest_row > 0 && dest_row < (*dest).height
158:       && src_col > 0 && src_col < (*src).width
159:       && src_row > 0 && src_row < (*src).height)
160:    {
161:      (*dest).data[dest_row][dest_col].red =
162:        (*src).data[src_row][src_col].red;
163:  
164:      (*dest).data[dest_row][dest_col].green =
165:        (*src).data[src_row][src_col].green;
166:  
167:      (*dest).data[dest_row][dest_col].blue =
168:        (*src).data[src_row][src_col].blue;
169:    }
170:  } 
171:  
172:  int rgb_avg(RGB_INT cur_pxl)
173:  {
174:     /*convert each RGB to the average of the original*/
175:     return ((cur_pxl.red + cur_pxl.green + cur_pxl.blue) / 3);
176:  }
177:  
178:  /*1st: pixel one of type RGB_INT
179:  2nd: pixel one of type RGB_INT
180:  3rd: differnce allowed to be considered "equal" or close enough*/
181:  int pxlcmp (RGB_INT pxl1, RGB_INT pxl2, int range)
182:  {
183:    return ((abs((rgb_avg(pxl1) - rgb_avg(pxl2)))) < range);
184:  }
185:  
186:  /* ================================================================= 
187:   * drawString - outputs a string of characters to the graphics port
188:   *
189:   *   x, y:      defines the starting location to draw the text
190:   *               note: this point is the lower left anchor of
191:   *                     the first character - a character's decending
192:   *                     portion would be drawn below this point.
193:   *   theFont:   points to the glut font to be used
194:   *   theString: holds the string to be output -- up to 255 ch
195:   * ----------------------------------------------------------------- */
196:  void drawString(int ix, int iy, void *theFont, char theString[256])
197:  {
198:     float x = (ix*2.0)/HSIZE - 1.0;
199:     float y = (iy*2.0)/VSIZE - 1.0;
200:     int i;
201:     glRasterPos2f(x, y);
202:     for (i = 0; theString[i] != '\0'; i++) /* draw the chars one at a time */
203:       glutBitmapCharacter(theFont, theString[i]);
204:  }
205:  
206:  /******CHAIN CODE************************************************
207:  ****************************************************************/
208:  
209:  int createNode(chainCode** first, chainCode** last, int new_x, int new_y,
210:  	       int new_dir)
211:  {
212:     chainCode* temp;
213:  
214:     if(!(temp = (chain_t)malloc(sizeof(struct chainCode))))
215:     {
216:       printf("error creating node\n");
217:        return FALSE;
218:     }
219:  
220:     temp->next = NULL;
221:     temp->code = new_dir;
222:     temp->location.x = new_x;
223:     temp->location.y = new_y;
224:     temp->prev = *last;
225:     if(*last)
226:       (*last)->next = temp;  /*now this is a good thing to set.*/
227:     *last = temp;  /*advance one*/
228:     
229:     if(*first == NULL)
230:       *first = *last;/*set this for the first node in the list once*/
231:  
232:     return TRUE;
233:  }
234:  
235:  /*returns TRUE if the chain code loops around itself, otherwise false*/
236:  int checkCode(chainCode* first, int x_current, int y_current,
237:  	      int x_check, int y_check, int dir)
238:  {
239:     struct chainCode* currentNode = first;
240:  
241:     while(currentNode && currentNode->next)
242:     {
243:        /*check two consedutive points*/
244:        if((currentNode->location.x == x_current) &&
245:           (currentNode->location.y == y_current) &&
246:           (currentNode->code == dir) &&
247:           (currentNode->next->location.x == x_check) &&
248:           (currentNode->next->location.y == y_check) &&
249:  	 (currentNode->next->next)) /*don't count the new node already*/
250:  	{
251:           return TRUE; /*The chain code end looped around*/
252:  	}
253:        else
254:           currentNode = currentNode->next;
255:     }
256:     return FALSE;
257:  }
258:  
259:  /*
260:  Preconditions:
261:  1st parameter: integer value containing the current x coordinate.
262:  2nd parameter: integer value containing the current y coordinate.
263:  3rd parameter: pointer to the pgm image being used.
264:  Postconditions:  return true if pixel is deemed boarder of object, false
265:     otherwise.*/
266:  int checkThings(int x_check, int y_check, PGMImage *img)
267:  {
268:  
269:     /*check for being in bounds*/
270:     if((x_check < 0) || (y_check < 0) || (x_check >= (*img).width) ||
271:        (y_check >= (*img).height))
272:        return FALSE;
273:  
274:     /*tests if the next pixel is greater than the threashing value.  This 
275:        value (cur_threash_val) should be set to the return value of 
276:        img_pxl_avg().  Also, checks if the pixel is considered in the background
277:        or forground agains the value in back_ground.  If so, return true to
278:        indicate create new node.*/
279:     if((rgb_avg((*img).data[y_check][x_check]) > cur_threash_val)
280:        && (back_ground < 0))
281:        return TRUE;   
282:  
283:     /*same thing as above, just for things less than, rathur than greater than*/
284:     if((rgb_avg((*img).data[y_check][x_check]) < cur_threash_val)
285:       && (back_ground > 0))
286:        return TRUE;
287:  
288:     return FALSE;
289:  }  
290:  
291:  coord neighborhood_point(int x_coord, int y_coord, int dir)
292:  {
293:     coord temp = {x_coord, y_coord};
294:  
295:     if(dir == EAST)
296:        temp.x++;
297:     else if(dir == NORTHEAST)
298:     {
299:        temp.x++;
300:        temp.y++;
301:     }
302:     else if(dir == NORTH)
303:        temp.y++;
304:     else if(dir == NORTHWEST)
305:     {
306:        temp.x--;
307:        temp.y++;
308:     }
309:     else if(dir == WEST)
310:        temp.x--;
311:     else if(dir == SOUTHWEST)
312:     {
313:        temp.x--;
314:        temp.y--;
315:     }
316:     else if(dir == SOUTH)
317:        temp.y--;
318:     else if(dir == SOUTHEAST)
319:     {
320:        temp.x++;
321:        temp.y--;
322:     }
323:  
324:     return temp;
325:  }
326:  
327:  /*determines the chain code for a starting pixel*/
328:  /*this chain code uses 8 connectivity*/
329:  /*Note: There are many different coordinate systems at work in this function.*/
330:  /*The (*img).data[][] are column major and have are therefore [y][x] or [j][i]*//*   This is also asuming (0,0) is bottom left, where the other code assumes
331:  /*   top left is (0, 0).  If it looks strange, look harder!!!*/
332:  /*Preconditions:
333:  1st parameter: pointer to the pgm image being used.
334:  2nd parameter: integer value containing the current x coordinate.
335:  3rd parameter: integer value containing the current y coordinate.
336:  4th parameter: integer value containing the current direction code.
337:  5th parameter: pointer to the last node of the chain code.
338:  6th parameter: pointer to the first node of the chain code.*/
339:  /*This function assumes the (0, 0) point is in the lower left*/
340:           
341:  int chaincode(PGMImage *img, int x, int y, int dir,
342:     struct chainCode** last, struct chainCode** first)   
343:  {
344:     int i; /*loop counting*/
345:     int next /*next direction to check*/, finished /*finished flag*/;
346:     coord next_point_to_check; /*next_point_to_check What else?*/
347:  
348:     next = (dir + 5) % 8; /*better initail choice?*/
349:  
350:  /*printf("next: %d  x: %d  y: %d\n", next, x, y);*/
351:     for(i = 0; i < 8; i++)
352:     {
353:        /*determine the next point to check*/
354:        next_point_to_check = neighborhood_point(x, y, next);
355:  
356:        if(checkThings(next_point_to_check.x, next_point_to_check.y, img))
357:        {
358:  /*setCPixel(next_point_to_check.x, next_point_to_check.y, blue);*/
359:  
360:  	 /*create the new node to go last in the chain*/
361:  	 createNode(first, last, next_point_to_check.x,
362:  		    next_point_to_check.y, next);
363:  
364:  	 /*if the next chaincode function in the recursion or the current state
365:           is the final state of the the chain code; recursively returns DONE
366:           back through all the levels to stop the chain code.*/
367:  	 if(checkCode(*first, x, y, next_point_to_check.x,
368:  		    next_point_to_check.y, dir))
369:  	 {
370:  	    return NONE;
371:  	 }
372:  
373:  	 if(NONE == chaincode(img, next_point_to_check.x,
374:  			      next_point_to_check.y, next, last, first))
375:  	    return NONE;
376:        }
377:  
378:        /*set next for next iteration*/
379:        next = (next + 1) % 8;
380:     }
381:     return FALSE;         
382:  }
383:  
384:  /*returns true if the point is already in one of the chains, false otherwise*/
385:  int alreadyFound(int initThreashFlag, int i, int j, chainCode** found)
386:  {
387:     int k; /*loop counting*/
388:     chainCode* temp; /*temporary node pointer*/
389:  
390:     /*while there are codes to check...*/
391:     for(k = 0; (temp = (found[k])) && k < MAX_NUMBER_OF_CHAINS; k++)
392:     {
393:        while(temp) /*...check them.*/
394:        {
395:  	 /*if point has already been found*/
396:  	 if((temp->location.x == i) && (temp->location.y == j))
397:  	 {
398:  	    return TRUE;
399:  	    break;
400:  	 } 
401:  	 temp = temp->next;
402:        }
403:     }
404:     
405:     return FALSE;
406:  }
407:  
408:  /*saves a chain code to file.  Was usefull during debuging now is just
409:    a cool thing to leave in*/
410:  /*1st: pointer to beginning of a chaincode
411:  2nd: the index of the chain code for destiqushing the 'chain' files*/
412:  void saveChainCode(chainCode* saveChain, int count)
413:  {
414:     struct chainCode* temp = saveChain;
415:     char filename[12];
416:     FILE* outfile;
417:     sprintf(filename, "chain%d", count);
418:     outfile = fopen(filename, "w");   /*output file for chaincode*/
419:     printf("Writing chain code to file %s.\n", filename);
420:  
421:     while(temp)
422:     {
423:     
424:        fprintf(outfile, "%d  %d  %d\n",temp->location.x,temp->location.y,
425:           temp->code);
426:        temp = temp->next;
427:     }
428:     fclose(outfile);
429:  }
430:  
431:  chainCode** showChain(PGMImage *original)
432:  {
433:     int i, j;  /*loop counting*/
434:     int count = cur_chain_count = 0; /*array index holder*/
435:     int initThreashFlag = img_pxl_avg(original);
436:     /*beginning points to an array of pointers.  Each pointer in the array is
437:       a pointer to the first node of MAX_NUMBER_OF_CHAINS number of chain
438:       codes*/
439:     struct chainCode** beginning, *chain = NULL;
440:      
441:     /*allocate an array of pointers to chainCodes, then initalize their
442:       values to NULL*/
443:     beginning = (chainCode**)
444:       malloc(MAX_NUMBER_OF_CHAINS * sizeof(chainCode*));
445:     memset(beginning, 0, MAX_NUMBER_OF_CHAINS * sizeof(chainCode*));
446:                
447:     cur_threash_val = initThreashFlag; /*set global variables*/
448:     back_ground = background(initThreashFlag, original);
449:  
450:     /*search image until a pixel is found with threasholded value of the
451:     object.  i & j will then contain the starting coordinate.*/
452:     for(i = 0; i < (*original).width; i++) /*x direction*/
453:     {
454:        for(j = (*original).height - 1; j >= 0; j--) /*y direction*/
455:        {
456:           /*skip to the next iteration if pixel isn't "good" enough*/
457:           if(!checkThings(i, j, original))
458:              continue;
459:  
460:           /*skip to the next iteration, which will be at the top of the next
461:             collumn.  This is true when the pixel has already been found in
462:  	   one of the chain codes.*/
463:           else if(alreadyFound(initThreashFlag, i, j, beginning))
464:  	    break;
465:  
466:           else
467:           {
468:  /*printf("chaincode: %d\n", count);*/
469:             /*printf("The starting coordinate is (x, y): (%d, %d)\n", i, j);*/
470:              chain = NULL; /*reset this to seperate the chaincodes*/
471:  
472:              /*find the chaincode for the current starting pixel*/
473:              chaincode(original, i, j, SOUTHEAST, &chain, &beginning[count]);
474:  	    if(beginning[count] != NULL) /*avoid writing zero length chains*/
475:  	    {
476:  	       saveChainCode(beginning[count], count);
477:  	       count++; /*advance the beginning counter*/
478:                 cur_chain_count = count;
479:  	    }
480:  
481:  	    /*force end of loops, leaving code when finished looping
482:  	      to still execute*/
483:  	    if(count >= MAX_NUMBER_OF_CHAINS)
484:  	       i = (*original).width;
485:  
486:              /*j = 0;*/ /*reset search to start at top on next pass.  This the
487:                       setting for stopping condition for the current j loop.*/
488:              break; /*Quick fix?*/
489:           }
490:        }   
491:     }
492:     printf("Done finding chain code(s).  %d were found.\n", count);
493:     return beginning;
494:  }
495:  
496:  /*****BOUNDRY BOX**********************************************************
497:   **********************************************************************/
498:     
499:  /*takes two coordinates as x and y pairs and returns the distence betweem them
500:     as a decimal*/
501:  float findDist(int x1, int y1, int x2, int y2)
502:  {
503:     return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
504:  }
505:  
506:  /*1st param: The array of coords that the first point of the major axis is
507:     returned in.
508:  2nd: The array of coords that the second point of the major axis is
509:     returned in.
510:  3rd: The array of chain codes that are searched through to find the major
511:     axes.*/
512:  /* Note: the ending condition is when a NULL chainCodes value is reached.
513:     Thusly, it works like a string requiring it to have the last legal value
514:     followed by a null value.*/
515:  void findFirstTwoCorners(coord corner1[], coord corner2[],
516:  			 chainCode** chainCodes)
517:  {
518:     int i; /*loop counting*/
519:     chainCode *temp, *search;
520:     double max_dist, test_dist;
521:  
522:     printf("\nFinding first 2 corners.\n");
523:  
524:     /*as long as there are codes to check, keep checking.  Note: the ending
525:     condition is when a NULL chainCodes value is reached.  Thusly, it works
526:     like a string requiring it to have the last legal value followed by a
527:     null value.*/
528:     for(i = 0; (temp = chainCodes[i]) && (i < MAX_CHAINS); i++)
529:     {      
530:        max_dist = 0.0;  /*reset this for each iteration*/
531:  
532:  /*printf("checking list: %d\n", i);*/
533:  
534:        while(temp) /*while there are still nodes to check in the chain*/
535:        {
536:           search = temp; /*set the faster moving search pointer to temp,
537:                            this increases the effiecency a lot compared to
538:  			  setting it equal to the first node..*/
539:           while(search)
540:           {
541:  /*setCPixel(temp->location.x, temp->location.y, green);*/
542:  
543:              /*determine if found a new maximum distance between two locations*/
544:              if((test_dist = findDist(search->location.x, search->location.y,
545:                 temp->location.x, temp->location.y)) > max_dist)
546:              {
547:                 max_dist = test_dist;
548:                 corner1[i].x = temp->location.x;
549:                 corner1[i].y = temp->location.y;
550:                 corner2[i].x = search->location.x;
551:                 corner2[i].y = search->location.y;
552:              }
553:              search = search->next;
554:           }
555:           temp = temp->next;
556:        }
557:  /*printf("point1: %d  %d\n", max1[i].x, max1[i].y);
558:  printf("point2: %d  %d\n", max2[i].x, max2[i].y);*/
559:     }
560:  }
561:  
562:  /*1st param: Array of coords for the first corner of each chain code.
563:  2nd param: Array of coords for the second corner of each chain code.
564:  The first two parameters should equal the first two parameters "returned"
565:  from the findFirstTwoCorners() function.
566:  3rd: Array of coords "returned" with the third corners.
567:  4th:  Array of coords "returned" with the fourth corners.
568:  5th: Pointer pointing to the array of chaincode pointers, obtained from
569:    showChain().*/
570:  void findSecondTwoCorners(coord corner1[], coord corner2[], coord corner3[],
571:  		 coord corner4[], chainCode** chain_code_array)
572:  {
573:     int i; /*loop counting*/
574:     chainCode* temp;
575:     float temp_dist1, temp_dist2; /*distance between point and each corner*/
576:     coord canidate_coord1, temp_coord;
577:     float canidate_dist1, max_dist;
578:     int corner_count;
579:  
580:     printf("\nFinding last 2 corners.\n");
581:  
582:     /*for each chain code find the corners*/
583:     for(i = 0; chain_code_array[i] && i < MAX_CHAINS; i++)
584:     {
585:        temp = chain_code_array[i];
586:        
587:        /*reset these for the next chain code*/
588:        max_dist = 0.0;
589:        corner_count = 1;
590:  
591:        while(temp) /*while there are nodes in the chain code to check*/
592:        {
593:  /*setCPixel(temp->location.x, temp->location.y, color1);*/
594:  
595:  	/*determine the first canidate coord for corner 3/4*/
596:  	if(((temp->location.x == corner1[i].x)
597:  	    && (temp->location.y == corner1[i].y))
598:  	   || ((temp->location.x == corner2[i].x)
599:  	       && (temp->location.y == corner2[i].y)))
600:  	{
601:  	  /*if this corner found is the first of the two allready known
602:  	    corners, then set the first canidate coord data and reset data
603:  	    to find the next canidate corner point*/
604:  	   if(corner_count == 1)
605:  	   {
606:  	      canidate_coord1.x = temp_coord.x;
607:  	      canidate_coord1.y = temp_coord.y;
608:  	      canidate_dist1 = max_dist;
609:  
610:  	      corner_count = 2; /*set for next corner*/
611:  	      max_dist = 0.0;
612:  	   }
613:  	   else if(corner_count == 2)
614:  	   {
615:  	      /*the second canidate is always a corner*/
616:  	      corner4[i].x = temp_coord.x;
617:  	      corner4[i].y = temp_coord.y;
618:  	      
619:  	      max_dist = 0.0; /*set for next corner canidate*/
620:  	   }
621:  	}
622:  
623:  	/*calculate the distance between the current point being checked and
624:  	  each corner point*/
625:  	temp_dist1 = findDist(corner1[i].x, corner1[i].y, temp->location.x,
626:  			      temp->location.y);
627:  	temp_dist2 = findDist(corner2[i].x, corner2[i].y, temp->location.x,
628:  			      temp->location.y);
629:  
630:  	/*if the current point is the furthest away sofar, store this point
631:  	  untill it is overridden or becomes a canidate point*/
632:  	if((temp_dist1 + temp_dist2) > max_dist)
633:  	{
634:  	   temp_coord.x = temp->location.x;
635:  	   temp_coord.y = temp->location.y;
636:  	  
637:  	   max_dist = (temp_dist1 + temp_dist2);
638:  	}
639:  
640:  	temp = temp->next; /*advance*/
641:        }
642:  
643:        /*from the three canidate coords find the two real corners.*/
644:        /*the second canidate will always be a corner, must test 1 vs 3, where
645:  	three is in the variables temp_coord and max_dist.*/
646:        if(canidate_dist1 > max_dist) /*first canidate*/
647:        {
648:           corner3[i].x = canidate_coord1.x;
649:  	 corner3[i].y = canidate_coord1.y;
650:        }
651:        else /*third canidate*/
652:        {
653:           corner3[i].x = temp_coord.x;
654:  	 corner3[i].y = temp_coord.y;
655:        }
656:  /*printf("corner3: (%d, %d)  corner4: (%d, %d)\n", corner3[i].x,
657:    corner3[i].y, corner4[i].x, corner4[i].y);*/
658:     }
659:  }
660:  
661:  /*takes a pointer to an image, and a pointer pointing to an array of
662:    chain codes pointers, here each chainCode pointer needs to be accessed
663:    by calculating the memory address.*/
664:  void showBound(PGMImage *original, chainCode** chainCodes)
665:  {     
666:     int i;
667:     /*hold the points farthest away from each other for each object*/
668:     coord corner1[MAX_CHAINS], corner2[MAX_CHAINS];
669:     coord corner3[MAX_CHAINS], corner4[MAX_CHAINS];
670:  
671:     /*find the first two corners.  they will be across a diagnal.*/
672:     findFirstTwoCorners(corner1, corner2, chainCodes);
673:     /*find the second two corners.  they will be across a diagnal too.*/
674:     findSecondTwoCorners(corner1, corner2, corner3, corner4, chainCodes);
675:          
676:     for(i = 0; chainCodes[i] && i < MAX_CHAINS; i++) /*for each chaincode*/
677:     {
678:        /*draw the boundry box*/
679:        setCLines(corner1[i].x, corner1[i].y, corner3[i].x, corner3[i].y,yellow);
680:        setCLines(corner4[i].x, corner4[i].y, corner2[i].x, corner2[i].y,yellow);
681:        setCLines(corner3[i].x, corner3[i].y, corner2[i].x, corner2[i].y,yellow);
682:        setCLines(corner4[i].x, corner4[i].y, corner1[i].x, corner1[i].y,yellow);
683:        setCPixel(corner1[i].x, corner1[i].y, purple);
684:        setCPixel(corner2[i].x, corner2[i].y, purple);
685:        setCPixel(corner3[i].x, corner3[i].y, purple);
686:        setCPixel(corner4[i].x, corner4[i].y, purple);
687:     }
688:  }
689:  
690:  
691:  /**********************Support functions*******************************/
692:  /***********************************************************************/
693:  
694:  /*Returns average (with RGB avg) pixel value for the image passed in.*/
695:  int img_pxl_avg(PGMImage* img)
696:  {
697:     int i, j; /*loop counting*/
698:     int sum = 0;
699:  
700:     for(i = 0; i < (*img).height; i++)/*collumn*/
701:        for(j = 0; j < (*img).width; j++)/*row*/
702:           sum += rgb_avg((*img).data[i][j]);
703:  
704:     return (sum / ((*img).height * (*img).height));
705:  }
706:  
707:  /*return >0 if number of pixels is greater than img. pxl. avg., return <0 if
708:    number of pixesl is less than img. pxl. avg. and return zero of equal*/
709:  int background(int treash_value, PGMImage* img)
710:  {
711:     int i, j; /*loop counting*/
712:     int pxl_less = 0, pxl_more = 0;
713:  
714:     for(i = 0; i < (*img).height; i++)/*collumn*/
715:        for(j = 0; j < (*img).width; j++)/*row*/
716:        {
717:  	 if(rgb_avg((*img).data[i][j]) < treash_value)
718:  	   pxl_less++;
719:  
720:           if(rgb_avg((*img).data[i][j]) > treash_value)
721:  	   pxl_more++;
722:        }
723:  
724:     if(pxl_less > pxl_more)
725:        return -1;
726:     else if(pxl_less < pxl_more)
727:        return 1;
728:     else
729:        return 0;
730:  }
731:  
732:  /**********************File I/O functions*******************************/
733:  /***********************************************************************/
734:  
735:  /*Gets an ascii color pgm image file (type P3).*/
736:  void getPGMfile (char filename[], PGMImage *img)
737:  {
738:    FILE *in_file;
739:    char ch;
740:    int row, col;
741:  
742:    in_file = fopen(filename, "r");
743:    if (in_file == NULL)
744:    {
745:      fprintf(stderr, "Error: Unable to open file %s\n\n", filename);
746:      exit(8);
747:    }
748:  
749:    printf("\nReading image file: %s", filename);
750:    
751:    do                                        /* skip header identifier */
752:      ch = getc(in_file);
753:    while (ch != '\n');
754:  
755:    do                                        /* skip comments lines    */
756:    {
757:      while (ch != '\n') ch = getc(in_file);  /*  flush to end of line  */
758:      ch = getc(in_file);
759:    } while (ch == '#');
760:   
761:    fseek(in_file, -1, SEEK_CUR);             /* backup one character   */
762:  
763:    fscanf(in_file,"%d", &((*img).width));
764:    fscanf(in_file,"%d", &((*img).height));
765:    fscanf(in_file,"%d", &((*img).maxVal));
766:  
767:    printf("\n width  = %d",(*img).width);
768:    printf("\n height = %d",(*img).height);
769:    printf("\n maxVal = %d",(*img).maxVal);
770:    printf("\n");
771:   
772:    if (((*img).width > MAX) || ((*img).height > MAX))
773:    {
774:      printf("\n\n***ERROR - image too big for current image structure***\n\n");
775:      exit(0);
776:    }
777:  
778:    for (row=(*img).height-1; row>=0; row--)
779:      for (col=0; col<(*img).width; col++)
780:      {
781:        fscanf(in_file,"%d", &((*img).data[row][col].red) );
782:        fscanf(in_file,"%d", &((*img).data[row][col].green));
783:        fscanf(in_file,"%d", &((*img).data[row][col].blue));
784:      }
785:    fclose(in_file);
786:    printf("\nDone reading file.\n");
787:  }
788:  
789:  
790:  void save(PGMImage *img)
791:  {
792:     int i, j, nr, nc, k;
793:     int red, green, blue;
794:     FILE *iop;
795:  
796:     nr = img->height;
797:     nc = img->width;
798:      
799:     iop = fopen("image1.pgm", "w");
800:     fprintf(iop, "P3\n");
801:     fprintf(iop, "%d %d\n", nc, nr);
802:     fprintf(iop, "255\n");
803:      
804:     k = 1;
805:     for(i = nr - 1; i >= 0; i--)
806:     {
807:        for(j = 0; j < nc; j++)
808:        {
809:           red = img->data[i][j].red;
810:           green = img->data[i][j].green;
811:           blue = img->data[i][j].blue;
812:           if(red < 0)
813:           {
814:              printf("IMG_WRITE: Found value %d at row %d col %d\n", red, i, j);
815:              printf("           Setting red to zero\n");
816:              red = 0;
817:           }
818:           if(green < 0)   
819:           {
820:              printf("IMG_WRITE: Found value %d at row %d col %d\n", green,i, j);
821:              printf("           Setting green to zero\n");
822:              green = 0;
823:           }
824:           if(blue < 0)   
825:           {
826:              printf("IMG_WRITE: Found value %d at row %d col %d\n", blue, i, j);
827:              printf("           Setting green to zero\n");
828:              blue = 0;
829:           }
830:           if(red > 255) 
831:           {   
832:              printf("IMG_WRITE: Found value %d at row %d col %d\n", red, i, j);
833:              printf("           Setting red to 255\n");
834:              red = 255;
835:           }
836:           if(green > 255)
837:           {
838:              printf("IMG_WRITE: Found value %d at row %d col %d\n", green,i, j);
839:              printf("           Setting green to 255\n");
840:              green = 255;
841:           }
842:           if(blue > 255)
843:           {
844:              printf("IMG_WRITE: Found value %d at row %d col %d\n", blue, i, j);
845:              printf("           Setting blue to 255\n");
846:              blue = 255;
847:           }
848:  
849:           if(k % 10)
850:           {
851:              fprintf(iop, "%d ", red);
852:              fprintf(iop, "%d ", green);
853:              fprintf(iop, "%d ", blue);
854:           }
855:           else /*for newline*/
856:           {
857:              fprintf(iop, "%d\n", red);
858:              fprintf(iop, "%d\n", green);
859:              fprintf(iop, "%d\n", blue);
860:           }
861:           k++;
862:        }
863:     }
864:     fprintf(iop, "\n");
865:     fclose(iop);
866:  }
867:  
868:  
869:  
870:  
871:  /****Calculation & drawing functions of the image translations**********
872:  ***********************************************************************/
873:              
874:            
875:  void showColor (PGMImage *img)
876:  {
877:     int row, col; /*y, x*/
878:     /*for (row=(*img).height-1; row>=0; row--)
879:        for (col=0; col<(*img).width; col++)
880:        {
881:  	 
882:  	 setCPixel(col, row, (*img).data[row][col]);
883:  	 }*/
884:  
885:     int i, j; /*loop counting: i = x, j = y*/
886:     int mid_width = ((*img).width / 2);
887:     int mid_height = ((*img).height / 2);
888:  
889:     for(i = 0; i < mid_width / 2; i++)
890:     {
891:        for(j = 0; j < mid_height; j++)
892:        {
893:  	/*inorder they are:
894:  	  bottom left, bottom middle right, top left, top m right,
895:  	  bottom middle left, bottom right, top middle left, top right.*/
896:  
897:  	 setCPixel(i, j, (*img).data[j][i]);
898:  	 setCPixel(i + mid_width, j, (*img).data[j][i + mid_width]);
899:  	 setCPixel(i, j + mid_height, (*img).data[j + mid_height][i]);
900:  	 setCPixel(i + mid_width, j + mid_height,
901:  		   (*img).data[j + mid_height][i + mid_width]);
902:  
903:  	 setCPixel(mid_width - i - 1, j,
904:  		   (*img).data[j][mid_width - i - 1]);
905:  	 setCPixel((*img).width - i - 1, j,
906:  		   (*img).data[j][(*img).width - i - 1]);
907:  	 setCPixel(mid_width - i - 1, (*img).height - j - 1,
908:  		   (*img).data[(*img).height - j - 1][mid_width - i - 1]);
909:  	 setCPixel((*img).width - i - 1, (*img).height - j - 1,
910:  		   (*img).data[(*img).height - j - 1][(*img).width - i - 1]);
911:        }
912:      }
913:      glFlush();
914:  }
915:  
916:  void camera_correction(PGMImage* new_img, PGMImage* org_img)
917:  {
918:     int row, col, img_row, img_col; /*loop counting*/
919:  
920:     /*camera parameters*/
921:     float height = 30; /*height of camera in cm*/
922:     float gamma = 0, theta = .698; /*camera angles = 40 degrees in rad*/
923:     float aperture = 1.1968, alpha = .598; /*aperature = 2 * alpha*/
924:     
925:     /*temporary varaibles*/
926:     float angular_height_corr;
927:     float angular_side_corr;
928:     int x_coord, y_coord;
929:  
930:     memset(new_img, 0, sizeof(PGMImage));
931:     (*new_img).height = (*org_img).height;
932:     (*new_img).width = (*org_img).width;
933:     (*new_img).maxVal = (*org_img).maxVal;
934:  
935:     for (row=(*org_img).height-1; row>=0; row--)
936:        for (col=0; col<(*org_img).width; col++)
937:        {
938:  	/*img_row -= (*org_img).height / 2;
939:  	  img_col = col - (*org_img).width / 2;*/
940:  
941:  	 angular_height_corr = 
942:     (theta - alpha) + col * (aperture / ((float)((*org_img).width) - 1));
943:  	 angular_side_corr =
944:     (gamma - alpha) + row * (aperture / ((float)((*org_img).height) - 1));
945:  
946:  	 angular_height_corr /= 2;
947:  	 /*angular_side_corr /= 2;*/
948:           /*height *= 2;*/
949:  	 height = 150;
950:  
951:  	 x_coord = (int)
952:  	   (height * (1 / tan(angular_height_corr)) * cos(angular_side_corr));
953:  	 y_coord = (int)
954:  	   (height * (1 / tan(angular_height_corr)) * sin(angular_side_corr));
955:  
956:  	 /*x_coord += (*org_img).width / 2;*/
957:  	 y_coord += (*org_img).height / 2;
958:  
959:  	 /*printf("org: (%d, %d)  new: (%d, %d)\n\n", row, col, y_coord, x_coord);*/
960:  
961:  	 pxlcpy(new_img, y_coord, x_coord, org_img, row, col);
962:        }
963:  }
964:  
965:  void new_corr(PGMImage* new_img, PGMImage* org_img)
966:  {
967:     /*i and j are the left half, k and l are the right half*/
968:     float i, k; /*loop counting*/
969:     int j, l, row; /*loop counting*/
970:     int old_i, old_k;
971:  
972:     float ins_s = 2; /*insert constant starting value*/
973:     float ins_k = ins_s; /*insert constant*/
974:  
975:     /*The halfway marks in the width.*/
976:     int mid_width_left = ((*new_img).width / 2) - 1;
977:     int mid_width_right = ((*new_img).width / 2);
978:     
979:     /*just to be thourough clear the memory and reset maxes*/
980:     memset(new_img, 0, sizeof(PGMImage));
981:     (*new_img).height = (*org_img).height;
982:     (*new_img).width = (*org_img).width;
983:     (*new_img).maxVal = (*org_img).maxVal;
984:  
985:     /*Loop through each row from top to bottom...*/
986:     for(row = ((*new_img).height - 1); row >= 0; row--)
987:     {
988:        /*...reset moire interference removal counter...*/
989:        old_i = ((*new_img).width / 2) - 1;
990:        old_k = ((*new_img).width / 2);
991:  
992:        /*...so each half is ajusted to remove perspective effect...*/
993:        for(i = j = mid_width_left, k = l = mid_width_right
994:  	    ; i >= 0, j >= 0, k < (*new_img).width, l < (*new_img).width
995:  	    ; i -= ins_k, j--, k += ins_k, l++)
996:        {
997:  	 for(;old_i >= (int)i; old_i--)  /*...in the left half...*/ 
998:  	    pxlcpy(new_img, row, old_i, org_img, row, j);
999:  	 for(;old_k <= (int)k; old_k++)  /*...in the right half.*/
1000:  	    pxlcpy(new_img, row, old_k, org_img, row, l);
1001:        }
1002:        /*Move the new image x_coord pixel counter to next new image pixel*/
1003:        ins_k -= ((ins_s - 1.0) / (*new_img).height);
1004:     }
1005:  }
1006:  
1007:  void color_to_gray(PGMImage* new_img, PGMImage* org_img)
1008:  {
1009:     int row, col; /*loop counting*/
1010:     RGB_INT cur_pxl; /*current pixel*/
1011:  
1012:     (*new_img).height = (*org_img).height;
1013:     (*new_img).width = (*org_img).width;
1014:     (*new_img).maxVal = (*org_img).maxVal;
1015:  
1016:     /*Starting with the top row...*/
1017:     for(row = (*new_img).height - 1; row >= 0; row--)
1018:        for(col = 0; col < (*new_img).width - 1; col++)
1019:        {
1020:  	 cur_pxl = (*org_img).data[row][col]; /*more readable*/
1021:  	
1022:  	 /*convert each RGB to the average of the original*/
1023:  	 (*new_img).data[row][col].red =  rgb_avg(cur_pxl);
1024:  	 (*new_img).data[row][col].green =  rgb_avg(cur_pxl);
1025:  	 (*new_img).data[row][col].blue =  rgb_avg(cur_pxl);
1026:        }
1027:  }
1028:  
1029:  void moravec(PGMImage* new_img, PGMImage* org_img)
1030:  {
1031:     int row, col; /*loop counting*/
1032:     int i, j, k, l; /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/
1033:     int running_sum;
1034:     float K = .5; /*.125 according to org. formula, but .5 is brighter*/
1035:     int max_val = 0, row_max, col_max; /* max porportion value in image*/
1036:  
1037:     memset(new_img, 0, sizeof(PGMImage));
1038:     (*new_img).height = (*org_img).height;
1039:     (*new_img).width = (*org_img).width;
1040:     (*new_img).maxVal = (*org_img).maxVal;
1041:  
1042:     /*starting at the top row*/
1043:     for(row = (*new_img).height - 1 - 1; row > 0; row--)
1044:        for(col = 1; col < (*new_img).width - 1; col++) /*left col start*/
1045:        {
1046:  	 i = row;
1047:  	 j = col;
1048:  	 running_sum = 0;
1049:  
1050:  	 /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/
1051:  	 for(k = i - 1; k <= i + 1; k++) /*row*/
1052:  	   for(l = j - 1; l <= j + 1; l++) /*column*/
1053:  	     running_sum += abs(rgb_avg((*org_img).data[k][l]) -
1054:  				rgb_avg((*org_img).data[i][j]));
1055:  
1056:  	 /*assign the new pixel value*/
1057:  	 (*new_img).data[row][col].red = (int)(K * running_sum);
1058:  	 (*new_img).data[row][col].green = (int)(K * running_sum);
1059:  	 (*new_img).data[row][col].blue = (int)(K * running_sum);
1060:        } 
1061:  }
1062:  
1063:  void detect_corners(PGMImage* org_img)
1064:  {
1065:     /*pointer to an array of pointers which point to the first nodes in each of
1066:       the chain codes.*/ 
1067:     chainCode **chain_codes;
1068:  
1069:     glPointSize(4); /*make points more visible, if desired*/
1070:     glLineWidth(4);
1071:  
1072:     chain_codes = showChain(org_img);
1073:     showBound(org_img, chain_codes);
1074:  
1075:     glFlush();/*force drawing*/
1076:  }
1077:  
1078:  /* =================================================================
1079:   * Callback functions.
1080:   *
1081:   * color = displayed graphics in window
1082:   * menu = menu event handling
1083:   * keyboard = deyboard event handling
1084:   * ----------------------------------------------------------------- */
1085:  void color(void)
1086:  {
1087:    /*glClear (GL_COLOR_BUFFER_BIT);*/
1088:     
1089:     /*  printf("\nDrawing Original image...\n");*/
1090:    showColor(img_cur);
1091:     
1092:    /*glFlush();*/
1093:  }
1094:  
1095:  #define RESTART 0
1096:  #define CAMERA_CORRECTION 1
1097:  #define X2_C_CORR 2
1098:  #define NEW_CORR 3
1099:  #define COLOR_TO_GRAY 4
1100:  #define MORAVEC 5
1101:  #define CORNERS 6
1102:  
1103:  void menu(int selection)
1104:  {
1105:     if(selection == RESTART)   
1106:     {
1107:        memcpy(img1, img0, sizeof(PGMImage));
1108:        img_cur = img0;
1109:     }
1110:     if(selection == CAMERA_CORRECTION)
1111:     {
1112:        printf("Starting camera correction\n");
1113:        camera_correction(img1, img0);
1114:        img_cur = img1;
1115:      }
1116:      if(selection == X2_C_CORR)
1117:      {
1118:         printf("Starting camera correction\n");
1119:         camera_correction(img1, img0);
1120:         camera_correction(img2, img1);
1121:         img_cur = img2;
1122:      }
1123:      if(selection == NEW_CORR)
1124:      {
1125:         new_corr(img1, img0);
1126:         img_cur = img1;
1127:      }
1128:      if(selection == COLOR_TO_GRAY)
1129:      {
1130:         color_to_gray(img1, img0);
1131:         img_cur = img1;
1132:      }
1133:      if(selection == MORAVEC)
1134:      {
1135:         moravec(img1, img0);
1136:         img_cur = img1;
1137:      }
1138:      if(selection == CORNERS)
1139:      {
1140:  printf("img avg: %d\n", img_pxl_avg(img0));
1141:        new_corr(img1, img0);
1142:         /*moravec(img2, img1);*/
1143:        showColor(img1);
1144:         detect_corners(img1);
1145:         img_cur = img1;
1146:         return;
1147:      }
1148:      /*glClear (GL_COLOR_BUFFER_BIT);*/
1149:      showColor(img_cur);
1150:      glutPostRedisplay();
1151:  }
1152:  
1153:  void keyboard(unsigned char key, int x, int y)
1154:  { 
1155:     switch (key)
1156:     {
1157:        case 27: 
1158:           exit(0);
1159:           break;
1160:     }
1161:  }
1162:  
1163:  void mouse(int button, int state, int x, int y)
1164:  {
1165:    char temp[50];
1166:    
1167:    if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
1168:    {
1169:       sprintf(temp, "(x, y): (%d, %d)  red: %d green: %d blue: %d\n",
1170:  	     x, VSIZE - y, (*img_cur).data[VSIZE - y][x].red,
1171:  	     (*img_cur).data[VSIZE - y][x].green,
1172:  	     (*img_cur).data[VSIZE - y][x].blue);
1173:       setCRect(0, 0, 200, 12, black);
1174:       glColor3f(1.0, 0.0, 0.0);
1175:       drawString(0, 0, GLUT_BITMAP_TIMES_ROMAN_10, temp);
1176:       glFlush();
1177:    }
1178:  }
1179:    
1180:  /* =================================================================
1181:   * init - initializes graphics viewport
1182:   *
1183:   * You should not have to change this function for the first few
1184:   * projects we have. It will become more important when we move to
1185:   * 3D graphics.
1186:   * ----------------------------------------------------------------- */
1187:  void init (void)
1188:  {
1189:    
1190:  /*
1191:   * select clearing color - white
1192:   */
1193:     glClearColor (1.0, 1.0, 1.0, 0.0);
1194:  
1195:  /*
1196:   * initialize viewport values
1197:   */
1198:     glMatrixMode(GL_PROJECTION);
1199:     glLoadIdentity();
1200:     glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
1201:  
1202:     /*add menus*/
1203:     glutCreateMenu(menu);
1204:     glutAddMenuEntry("Restart", RESTART);
1205:     glutAddMenuEntry("Camera Correction", CAMERA_CORRECTION);
1206:     glutAddMenuEntry("2x C. Corr.", X2_C_CORR);
1207:     glutAddMenuEntry("new corr", NEW_CORR);
1208:     glutAddMenuEntry("color to gray", COLOR_TO_GRAY);
1209:     glutAddMenuEntry("moravec", MORAVEC);
1210:     glutAddMenuEntry("corners", CORNERS);
1211:     glutAttachMenu(GLUT_RIGHT_BUTTON);
1212:  }
1213:   
1214:  int main(int argc, char** argv)
1215:  {
1216:     char PGMfileName[MAX_FILE_LENGTH];
1217:     
1218:     int WindowID;
1219:  
1220:     int i;  /*looping variable*/
1221:      
1222:     /*parse the command line*/  
1223:     if(argc == 1)
1224:     {
1225:        printf("To few parameters.\n");
1226:        printf("Usage: research <file.pgm>\n");
1227:        exit(1);
1228:     }
1229:     else if(argc == 2)
1230:        strcpy(PGMfileName, argv[1]);
1231:     else
1232:     {
1233:        printf("To many parameters.\n");
1234:        printf("Usage: research <file.pgm>\n");
1235:        exit(1);
1236:     }
1237:  /*
1238:   * Read in image file. - note: sets our global values, too.
1239:   * ----------------------------------------------------------------- */
1240:               
1241:     img0 = (PGMImage*) malloc(sizeof(PGMImage));
1242:     getPGMfile(PGMfileName, img0);
1243:     HSIZE = (*img0).width;
1244:     VSIZE = (*img0).height;
1245:     MVAL = (*img0).maxVal;
1246:  
1247:     img_cur = img0; /*VERY IMPORTANT to set this*/
1248:              
1249:     /*allocate memory for second image*/
1250:     img1 = (PGMImage*) malloc(sizeof(PGMImage));
1251:     memcpy(img1, img0, sizeof(PGMImage));
1252:     /*(*img1).width = HSIZE;
1253:     (*img1).height = VSIZE;
1254:     (*img1).maxVal = 255;*/
1255:               
1256:     img2 = (PGMImage*) malloc(sizeof(PGMImage));
1257:     memcpy(img2, img0, sizeof(PGMImage));
1258:              
1259:  /*
1260:   * Initialize the glut package.
1261:   * ----------------------------------------------------------------- */
1262:     glutInit(&argc, argv);
1263:     glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
1264:  /*           
1265:   * Define a new window (its size, position and title).
1266:   * ----------------------------------------------------------------- */
1267:        glutInitWindowSize (HSIZE, VSIZE);  /*size*/
1268:        glutInitWindowPosition (10, 10);    /*position*/
1269:        WindowID = glutCreateWindow (PGMfileName); /*title*/
1270:        glutSetWindow(WindowID);
1271:        glutDisplayFunc(color);
1272:        
1273:  /*  
1274:   * Call our init function to define viewing parameters.
1275:   * ----------------------------------------------------------------- */
1276:     init ();
1277:     
1278:     glutMouseFunc(mouse);
1279:     glutKeyboardFunc(keyboard); 
1280:     glutMainLoop();
1281:  
1282:  /*  
1283:   * When we reach here, we've left the event loop and are ready to
1284:   * exit.
1285:   * ----------------------------------------------------------------- */
1286:     return 0;
1287:  }
1288:  
1289:  
1290:  
1291:  
1292:  
1293:  
1294:  
1295:  
1296:  
1297:  
1298:  
1299:  
1300: