1:  #include <stdlib.h>
2:  #include <stdio.h>
3:  #include <string.h>
4:  #include <math.h>
5:  #include <GL/glut.h>
6:  
7:  #include "pgm.h"
8:  #include "chaincode.h"
9:  
10:  #define Round(v) ((int)(v+0.5))
11:  
12:  /*minimal size to consider an "object" as length of chaincode*/
13:  #define DECENT_SIZED_OBJECT 100
14:  
15:  #define ONE_THIRD (1.0 / 3.0)
16:  #define ONE_HALF  (1.0 / 2.0)
17:  #define ONE_SIXTH (1.0 / 6.0)
18:  
19:  typedef struct
20:  {
21:    coord corner1;
22:    coord corner2;
23:    coord corner3;
24:    coord corner4;
25:  }object;
26:  
27:  typedef struct
28:  {
29:     /*The corners of the board.  corner1 and corner2 are always opposite
30:       (diagnol), just like corner3 and corner4 too.*/
31:     object corners;
32:  
33:     /*The sides.  The numbers represent the to corners that the point is
34:      between.  The first number is the corner that it is closest to.*/
35:     coord side14, side13, side23, side24;
36:     coord side31, side32, side41, side42;
37:     
38:     /*The points surrounding the middle square.  The number indicates the
39:       closest corner.*/
40:     coord middle1, middle2, middle3, middle4;
41:  }ttt;
42:  
43:  /*Evil globals, but not do-able otherwise.*/
44:  static PGMImage *img_cur;       /*current*/
45:  static PGMImage *img_original;  /*original*/
46:  static PGMImage *img_pers_corr; /*perspective correction*/
47:  static PGMImage *img_grayscale;
48:  static PGMImage *img_moravec;   /*moravec*/
49:  static int HSIZE;
50:  static int VSIZE;
51:  static int MVAL;
52:  
53:  static ttt *ttt_board; /*pointer to a ttt structer containing the board info*/
54:  
55:  const RGB_INT white      = {255, 255, 255};
56:  const RGB_INT yellow     = {255, 255,   0};
57:  const RGB_INT magenta    = {255,   0, 255};
58:  const RGB_INT cyan       = {0,   255, 255};
59:  const RGB_INT red        = {255,   0,   0};
60:  const RGB_INT green      = {0,   255,   0};
61:  const RGB_INT blue       = {0,     0, 255};
62:  const RGB_INT black      = {0,     0,   0};
63:  
64:  const RGB_INT gray       = {128, 128, 128};
65:  const RGB_INT lt_yellow  = {255, 255, 128};
66:  const RGB_INT lt_magenta = {255, 128, 255};
67:  const RGB_INT lt_cyan    = {128, 255, 255};
68:  
69:  const RGB_INT lt_red     = {255, 128, 128};
70:  const RGB_INT lt_green   = {128, 255, 128};
71:  const RGB_INT lt_blue    = {128, 128, 255};
72:  
73:  const RGB_INT dk_yellow  = {128, 128,   0};
74:  const RGB_INT dk_magenta = {128,   0, 128};
75:  const RGB_INT dk_cyan    = {  0, 128, 128};
76:  
77:  const RGB_INT dk_red     = {128,   0,   0};
78:  const RGB_INT dk_green   = {  0, 128,   0};
79:  const RGB_INT dk_blue    = {  0,   0, 128};
80:  
81:  /*pointer to an array of pointers which point to the first nodes in each of
82:    the chain codes.*/ 
83:  list_info* chain_codes;
84:  
85:  /*hold the points farthest away from each other for each object*/
86:  /*coord corner1[MAX_CHAINS], corner2[MAX_CHAINS];
87:    coord corner3[MAX_CHAINS], corner4[MAX_CHAINS];*/
88:  /*object all_objects[MAX_CHAINS];*/
89:  static object *all_objects;
90:  
91:  /*use double buffered output and keep running enable idle callback to call
92:    detect_corners() over and over simulating realtime response.
93:    Is set to TRUE or FALSE.*/
94:  int is_buffered;
95:  
96:  /*used to determine if abstract lines should be drawn to the screen.
97:    Set to <0 if no abstract lines are to be drawn.  Otherwise
98:    is set to the number of objects found (AKA number of chaincodes).*/
99:  int draw_abstract_lines = -1;
100:  
101:  /*used to draw the single object most likely to be considered the
102:    tic-tac-toe board.  Should be <0 if this should not be drawn.
103:    Should be equal to the chain-code number (from 0 to MAX_CHAINS - 1)
104:    if it is to be drawn.*/
105:  int draw_abstract_board = -1;
106:  
107:  
108:  /**************Drawing funcitions************************************/
109:  /********************************************************************/
110:  
111:  void setCPixel(int ix, int iy, RGB_INT color)
112:  /*Same as setIPixel except that the last parameter is an RGB color*/
113:  {
114:    float x = (ix*2.0)/HSIZE - 1.0;
115:    float y = (iy*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_POINTS); 
124:       glVertex2f (x, y);
125:    glEnd();
126:  }
127:  
128:  void setCLines(int ix1, int iy1, int ix2, int iy2, RGB_INT color)
129:  /*Similar as setIPixel except that this one draws a line between the first set
130:    of points given and the second set in the RGB color specified*/
131:  {
132:    float x1 = (ix1*2.0)/HSIZE - 1.0;
133:    float y1 = (iy1*2.0)/VSIZE - 1.0;
134:    float x2 = (ix2*2.0)/HSIZE - 1.0;
135:    float y2 = (iy2*2.0)/VSIZE - 1.0;
136:   
137:    float red = (float)color.red/(float)MVAL;
138:    float green = (float)color.green/(float)MVAL;
139:    float blue = (float)color.blue/(float)MVAL;
140:  
141:    glColor3f(red, green, blue);
142:  
143:    glBegin(GL_LINES);
144:      glVertex2f (x1, y1);
145:      glVertex2f (x2, y2);
146:    glEnd();
147:  }
148:  
149:  void setCRect(int ix1, int iy1, int ix2, int iy2, RGB_INT color)
150:  /*Similar as setIPixel except that this one draws a line between the first set
151:    of points given and the second set in the RGB color specified*/
152:  {
153:    float x1 = (ix1*2.0)/HSIZE - 1.0;
154:    float y1 = (iy1*2.0)/VSIZE - 1.0;
155:    float x2 = (ix2*2.0)/HSIZE - 1.0;
156:    float y2 = (iy2*2.0)/VSIZE - 1.0;
157:   
158:    float red = (float)color.red/(float)MVAL;
159:    float green = (float)color.green/(float)MVAL;
160:    float blue = (float)color.blue/(float)MVAL;
161:  
162:    glColor3f(red, green, blue);
163:  
164:    glBegin(GL_POLYGON);
165:      glVertex2f (x1, y1);
166:      glVertex2f (x1, y2);
167:      glVertex2f (x2, y2);
168:      glVertex2f (x2, y1);
169:    glEnd();
170:  }
171:  
172:  /* ================================================================= 
173:   * drawString - outputs a string of characters to the graphics port
174:   *
175:   *   x, y:      defines the starting location to draw the text
176:   *               note: this point is the lower left anchor of
177:   *                     the first character - a character's decending
178:   *                     portion would be drawn below this point.
179:   *   theFont:   points to the glut font to be used
180:   *   theString: holds the string to be output -- up to 255 ch
181:   * ----------------------------------------------------------------- */
182:  void drawString(int ix, int iy, void *theFont, char theString[256])
183:  {
184:     float x = (ix*2.0)/HSIZE - 1.0;
185:     float y = (iy*2.0)/VSIZE - 1.0;
186:     int i;
187:     glRasterPos2f(x, y);
188:     for (i = 0; theString[i] != '\0'; i++) /* draw the chars one at a time */
189:       glutBitmapCharacter(theFont, theString[i]);
190:  }
191:  
192:  void showColor (PGMImage *img)
193:  {
194:     int i, j; /*loop counting: i = y, j = x*/
195:  
196:     GLubyte checkImage[(*img).height][(*img).width][3];
197:  
198:        glPointSize(2); /*make points more visible, if desired*/
199:     /*glLineWidth(4);*/
200:  
201:     for(i = 0; i < (*img).height; i++)
202:     {
203:        for(j = 0; j < (*img).width; j++)
204:        {
205:           checkImage[i][j][0] = (GLubyte) (*img).data[i][j].red;
206:           checkImage[i][j][1] = (GLubyte) (*img).data[i][j].green;
207:           checkImage[i][j][2] = (GLubyte) (*img).data[i][j].blue;
208:        }
209:     }
210:     /*draw the current image*/
211:     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
212:     glRasterPos2f(-1, -1);
213:     glDrawPixels((*img).width, (*img).height, GL_RGB,
214:  		GL_UNSIGNED_BYTE, checkImage);
215:  
216:     glFlush();
217:  }
218:  
219:  /*display the global abstract data*/
220:  void showAbstract(object* object_list, ttt *ttt_data)
221:  {
222:     int i;
223:  
224:     coord hor_top1, hor_top2;
225:     coord hor_bot1, hor_bot2;
226:     coord ver_rgt1, ver_rgt2;
227:     coord ver_lft1, ver_lft2;
228:     
229:     list_info temp;
230:  
231:     /*draw the chaincode*/
232:     for(i = 0; (i < MAX_CHAINS) && chain_codes && chain_codes[i].cur; i++)
233:     {
234:        memcpy(&temp, &chain_codes[i], sizeof(list_info));
235:        while(RetrieveNextNode(&temp).cur)
236:        {
237:  	setCPixel(RetrieveInfo(&temp).location.x,
238:  		  RetrieveInfo(&temp).location.y, gray);
239:          Advance(&temp);
240:        }
241:     }
242:  
243:     /*first check for non-null pointer, next check for dereferenced pointers
244:       in the array of head pointers and lastly make sure things stay in
245:       bound of the max incase all MAX_CHAINS number of chains are used.*/
246:     /*draw_abstract_lines is the global that holds the number of "objects"
247:       to draw abstract information for*/
248:     for(i = 0; i < draw_abstract_lines && i < MAX_CHAINS; i++)
249:     {
250:        setCLines(object_list[i].corner1.x, object_list[i].corner1.y,
251:  		object_list[i].corner3.x, object_list[i].corner3.y,yellow);
252:        setCLines(object_list[i].corner4.x, object_list[i].corner4.y,
253:  		object_list[i].corner2.x, object_list[i].corner2.y,yellow);
254:        setCLines(object_list[i].corner3.x, object_list[i].corner3.y,
255:  		object_list[i].corner2.x, object_list[i].corner2.y,yellow);
256:        setCLines(object_list[i].corner4.x, object_list[i].corner4.y,
257:  		object_list[i].corner1.x, object_list[i].corner1.y,yellow);
258:        setCPixel(object_list[i].corner1.x, object_list[i].corner1.y, magenta);
259:        setCPixel(object_list[i].corner2.x, object_list[i].corner2.y, magenta);
260:        setCPixel(object_list[i].corner3.x, object_list[i].corner3.y, magenta);
261:        setCPixel(object_list[i].corner4.x, object_list[i].corner4.y, magenta);
262:     }
263:  
264:     /*if there is board to draw and just make sure there isn't a NULL pointer*/
265:     if((draw_abstract_board > -1) && ttt_data)
266:     {
267:        /*This code should draw the bounding box*/
268:       /*setCLines(ttt_data->corners.corner1.x, 
269:                  ttt_data->corners.corner1.y,
270:                  ttt_data->corners.corner3.x,
271:                  ttt_data->corners.corner3.y,blue);
272:        setCLines(ttt_data->corners.corner4.x,
273:                  ttt_data->corners.corner4.y,
274:                  ttt_data->corners.corner2.x,
275:                  ttt_data->corners.corner2.y,blue);
276:        setCLines(ttt_data->corners.corner3.x,
277:                  ttt_data->corners.corner3.y,
278:                  ttt_data->corners.corner2.x,
279:                  ttt_data->corners.corner2.y,blue);
280:        setCLines(ttt_data->corners.corner4.x,
281:                  ttt_data->corners.corner4.y,
282:                  ttt_data->corners.corner1.x,
283:                  ttt_data->corners.corner1.y,blue);
284:        setCPixel(ttt_data->corners.corner1.x,
285:                  ttt_data->corners.corner1.y, red);
286:        setCPixel(ttt_data->corners.corner2.x,
287:                  ttt_data->corners.corner2.y, red);
288:        setCPixel(ttt_data->corners.corner3.x,
289:                  ttt_data->corners.corner3.y, red);
290:        setCPixel(ttt_data->corners.corner4.x,
291:                  ttt_data->corners.corner4.y, red);
292:       */
293:  
294:        setCLines(ttt_data->side13.x,
295:                  ttt_data->side13.y,
296:                  ttt_data->side42.x,
297:                  ttt_data->side42.y,blue);
298:        setCLines(ttt_data->side31.x,
299:                  ttt_data->side31.y,
300:                  ttt_data->side24.x,
301:                  ttt_data->side24.y,blue);
302:        setCLines(ttt_data->side23.x,
303:                  ttt_data->side23.y,
304:                  ttt_data->side41.x,
305:                  ttt_data->side41.y,blue);
306:        setCLines(ttt_data->side32.x,
307:                  ttt_data->side32.y,
308:                  ttt_data->side14.x,
309:                  ttt_data->side14.y,blue);
310:     }
311:     glFlush();
312:  }
313:  
314:  /**********************Support functions*******************************/
315:  /***********************************************************************/
316:  
317:  void pxlcpy(PGMImage *dest, int dest_row, int dest_col,
318:  	    PGMImage *src, int src_row, int src_col)
319:  {
320:    /*make sure values are within bounds*/
321:    if(dest_col > 0 && dest_col < (*dest).width
322:       && dest_row > 0 && dest_row < (*dest).height
323:       && src_col > 0 && src_col < (*src).width
324:       && src_row > 0 && src_row < (*src).height)
325:    {
326:      (*dest).data[dest_row][dest_col].red =
327:        (*src).data[src_row][src_col].red;
328:  
329:      (*dest).data[dest_row][dest_col].green =
330:        (*src).data[src_row][src_col].green;
331:  
332:      (*dest).data[dest_row][dest_col].blue =
333:        (*src).data[src_row][src_col].blue;
334:    }
335:  } 
336:  
337:  int rgb_avg(RGB_INT cur_pxl)
338:  {
339:     /*convert each RGB to the average of the original*/
340:     return ((cur_pxl.red + cur_pxl.green + cur_pxl.blue) / 3);
341:  }
342:  
343:  /*Returns average (with RGB avg) pixel value for the image passed in.*/
344:  int img_pxl_avg(PGMImage* img)
345:  {
346:     int i, j; /*loop counting*/
347:     int sum = 0;
348:  
349:     for(i = 0; i < (*img).height; i++)/*collumn*/
350:        for(j = 0; j < (*img).width; j++)/*row*/
351:           sum += rgb_avg((*img).data[i][j]);
352:  
353:     return (sum / ((*img).height * (*img).height));
354:  }
355:  
356:  /*1st: pixel one of type RGB_INT
357:  2nd: pixel one of type RGB_INT
358:  3rd: differnce allowed to be considered "equal" or close enough*/
359:  int pxlcmp (RGB_INT pxl1, RGB_INT pxl2, int range)
360:  {
361:    return ((abs((rgb_avg(pxl1) - rgb_avg(pxl2)))) < range);
362:  }
363:  
364:  /*return >0 if number of pixels is greater than img. pxl. avg., return <0 if
365:    number of pixesl is less than img. pxl. avg. and return zero of equal*/
366:  int background(int treash_value, PGMImage* img)
367:  {
368:     int i, j; /*loop counting*/
369:     int pxl_less = 0, pxl_more = 0;
370:  
371:     for(i = 0; i < (*img).height; i++)/*collumn*/
372:        for(j = 0; j < (*img).width; j++)/*row*/
373:        {
374:  	 if(rgb_avg((*img).data[i][j]) < treash_value)
375:  	   pxl_less++;
376:  
377:           if(rgb_avg((*img).data[i][j]) > treash_value)
378:  	   pxl_more++;
379:        }
380:  
381:     if(pxl_less > pxl_more)
382:        return -1;
383:     else if(pxl_less < pxl_more)
384:        return 1;
385:     else
386:        return 0;
387:  }
388:  
389:  /*Used by showColor() and detect_pieces()*/
390:  coord find_dividers(coord point1, coord point2, float t)
391:  {
392:     coord temp;
393:     
394:     temp.x = (int)(((1.0 - t) * point1.x) + (t * point2.x));
395:     temp.y = (int)(((1.0 - t) * point1.y) + (t * point2.y));
396:     
397:     return temp;   
398:  }
399:  
400:  /******Perspective correction*******************************************
401:  ***********************************************************************/
402:  void pers_corr(PGMImage* new_img, PGMImage* org_img)
403:  {
404:     /*i and j are the left half, k and l are the right half*/
405:     float i, k; /*loop counting*/
406:     int j, l, row; /*loop counting*/
407:     int old_i, old_k;
408:  
409:     float ins_s = 2.0; /*insert constant starting value*/
410:     float ins_k = ins_s; /*insert constant*/
411:  
412:     /*The halfway marks in the width.*/
413:     int mid_width_left = ((*new_img).width / 2) - 1;
414:     int mid_width_right = ((*new_img).width / 2);
415:     
416:     /*just to be thourough clear the memory and reset maxes*/
417:     memset(new_img, 0, sizeof(PGMImage));
418:     (*new_img).height = (*org_img).height;
419:     (*new_img).width = (*org_img).width;
420:     (*new_img).maxVal = (*org_img).maxVal;
421:  
422:     /****x direction correction******/
423:  
424:     /*Loop through each row from top to bottom...*/
425:     for(row = ((*new_img).height - 1); row >= 0; row--)
426:     {
427:        /*...reset moire interference removal counter...*/
428:        old_i = ((*new_img).width / 2) - 1;
429:        old_k = ((*new_img).width / 2);
430:  
431:        /*...so each half is ajusted to remove perspective effect...*/
432:        for(i = j = mid_width_left, k = l = mid_width_right
433:  	    ; i >= 0, j >= 0, k < (*new_img).width, l < (*new_img).width
434:  	    ; i -= ins_k, j--, k += ins_k, l++)
435:        {
436:  	 for(;old_i >= (int)i; old_i--)  /*...in the left half...*/ 
437:  	    pxlcpy(new_img, row, old_i, org_img, row, j);
438:  	 for(;old_k <= (int)k; old_k++)  /*...in the right half.*/
439:  	    pxlcpy(new_img, row, old_k, org_img, row, l);
440:        }
441:        /*Move the new image x_coord pixel counter to next new image pixel*/
442:        ins_k -= ((ins_s - 1.0) / (*new_img).height);
443:     }
444:  }
445:  
446:  /*****convert color to grayscale****************************************
447:  ***********************************************************************/
448:  void color_to_gray(PGMImage* new_img, PGMImage* org_img)
449:  {
450:     int row, col; /*loop counting*/
451:     RGB_INT cur_pxl; /*current pixel*/
452:  
453:     (*new_img).height = (*org_img).height;
454:     (*new_img).width = (*org_img).width;
455:     (*new_img).maxVal = (*org_img).maxVal;
456:  
457:     /*Starting with the top row...*/
458:     for(row = (*new_img).height - 1; row >= 0; row--)
459:        for(col = 0; col < (*new_img).width - 1; col++)
460:        {
461:  	 cur_pxl = (*org_img).data[row][col]; /*more readable*/
462:  	
463:  	 /*convert each RGB to the average of the original*/
464:  	 (*new_img).data[row][col].red =  rgb_avg(cur_pxl);
465:  	 (*new_img).data[row][col].green =  rgb_avg(cur_pxl);
466:  	 (*new_img).data[row][col].blue =  rgb_avg(cur_pxl);
467:        }
468:  }
469:  
470:  /*******Edge highlighting**********************************************
471:  **********************************************************************/
472:  void moravec(PGMImage* new_img, PGMImage* org_img)
473:  {
474:     int row, col; /*loop counting*/
475:     int i, j, k, l; /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/
476:     int running_sum;
477:     float K = .5; /*.125 according to org. formula, but .5 is brighter*/
478:     int max_val = 0, row_max, col_max; /* max porportion value in image*/
479:  
480:     memset(new_img, 0, sizeof(PGMImage));
481:     (*new_img).height = (*org_img).height;
482:     (*new_img).width = (*org_img).width;
483:     (*new_img).maxVal = (*org_img).maxVal;
484:  
485:     /*starting at the top row*/
486:     for(row = (*new_img).height - 1 - 1; row > 0; row--)
487:        for(col = 1; col < (*new_img).width - 1; col++) /*left col start*/
488:        {
489:  	 i = row;
490:  	 j = col;
491:  	 running_sum = 0;
492:  
493:  	 /*Sanka, Hlavac & Boyle; p. 97 f. 4.73*/
494:  	 for(k = i - 1; k <= i + 1; k++) /*row*/
495:  	   for(l = j - 1; l <= j + 1; l++) /*column*/
496:  	     running_sum += abs(rgb_avg((*org_img).data[k][l]) -
497:  				rgb_avg((*org_img).data[i][j]));
498:  
499:  	 /*assign the new pixel value*/
500:  	 (*new_img).data[row][col].red = (int)(K * running_sum);
501:  	 (*new_img).data[row][col].green = (int)(K * running_sum);
502:  	 (*new_img).data[row][col].blue = (int)(K * running_sum);
503:        } 
504:  }
505:  
506:  /*******Corners*****************************************************
507:  *******************************************************************/
508:  
509:  /*takes two coordinates as x and y pairs and returns the distence betweem them
510:     as a decimal*/
511:  float findDist(int x1, int y1, int x2, int y2)
512:  {
513:     return sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
514:  }
515:           
516:  /*1st param: The array of coords that the first point of the major axis is
517:     returned in.
518:  2nd: The array of coords that the second point of the major axis is
519:     returned in.
520:  3rd: The array of chain codes that are searched through to find the major
521:     axes.*/
522:  /* Note: the ending condition is when a NULL chainCodes value is reached.
523:     Thusly, it works like a string requiring it to have the last legal value
524:     followed by a null value.*/
525:  void findFirstTwoCorners(object *objects_array, list_info* chainCodes)
526:  {
527:     int i; /*loop counting*/
528:     list_info temp, search;
529:     double max_dist, test_dist;
530:  
531:  /*printf("\nFinding first 2 corners.\n");*/
532:  
533:     /*as long as there are codes to check, keep checking.  Note: the ending
534:     condition is when a NULL chainCodes value is reached.  Thusly, it works
535:     like a string requiring it to have the last legal value followed by a
536:     null value.*/
537:     for(i = 0; ((i < MAX_CHAINS) && chainCodes[i].cur); i++)
538:     {      
539:        memcpy(&temp, &chainCodes[i], sizeof(list_info)); 
540:  
541:        max_dist = 0.0;  /*reset this for each iteration*/
542:  
543:  /*printf("checking list: %d\n", i);*/
544:  
545:        while(RetrieveNextNode(&temp).cur) /*while there are nodes to check*/
546:        {
547:  	 /*set the faster moving search pointer to temp,
548:  	   this increases the effiecency a lot compared to
549:  	   setting it equal to the first node..*/
550:           memcpy(&search, &temp, sizeof(list_info));
551:           
552:  	 while(RetrieveNextNode(&search).cur)
553:           {
554:  /*setCPixel(RetrieveInfo(&temp).location.x,
555:    RetrieveInfo(&temp).location.y, green);*/
556:  
557:              /*determine if found a new maximum distance between two locations*/
558:              if((test_dist = findDist(RetrieveInfo(&search).location.x,
559:  				     RetrieveInfo(&search).location.y,
560:  				     RetrieveInfo(&temp).location.x,
561:  				     RetrieveInfo(&temp).location.y))>max_dist)
562:              {
563:                 max_dist = test_dist;
564:                 objects_array[i].corner1.x = RetrieveInfo(&temp).location.x;
565:                 objects_array[i].corner1.y = RetrieveInfo(&temp).location.y;
566:                 objects_array[i].corner2.x = RetrieveInfo(&search).location.x;
567:                 objects_array[i].corner2.y = RetrieveInfo(&search).location.y;
568:              }
569:              Advance(&search);
570:           }
571:           Advance(&temp);
572:        }
573:  /*printf("point1: %d  %d\n", max1[i].x, max1[i].y);
574:  printf("point2: %d  %d\n", max2[i].x, max2[i].y);*/
575:     }
576:  }
577:  
578:  /*1st param: Array of coords for the first corner of each chain code.
579:  2nd param: Array of coords for the second corner of each chain code.
580:  The first two parameters should equal the first two parameters "returned"
581:  from the findFirstTwoCorners() function.
582:  3rd: Array of coords "returned" with the third corners.
583:  4th:  Array of coords "returned" with the fourth corners.
584:  5th: Pointer pointing to the array of chaincode pointers, obtained from
585:    showChain().*/
586:  void findSecondTwoCorners(object *objects_array, list_info* chain_code_array)
587:  {
588:     int i; /*loop counting*/
589:     list_info temp;
590:     float temp_dist1, temp_dist2; /*distance between point and each corner*/
591:     coord canidate_coord1, temp_coord;
592:     float canidate_dist1, max_dist;
593:     int corner_count;
594:  
595:  /*printf("\nFinding last 2 corners.\n\n");*/
596:  
597:     /*for each chain code find the corners*/
598:     for(i = 0; (i < MAX_CHAINS) && chain_code_array[i].cur; i++)
599:     {
600:        memcpy(&temp, &chain_code_array[i], sizeof(list_info));
601:        
602:        /*reset these for the next chain code*/
603:        max_dist = 0.0;
604:        corner_count = 1;
605:  
606:        /*while there are nodes in the chain code to check*/
607:        /*if there isn't a next node cur is NULL, which is checked*/
608:        while(RetrieveNextNode(&temp).cur)
609:        {
610:  /*setCPixel(RetrieveInfo(&temp).location.x,
611:    RetrieveInfo(&temp).location.y, color1);*/
612:  
613:  	/*determine the first canidate coord for corner 3/4*/
614:  	if(((RetrieveInfo(&temp).location.x == objects_array[i].corner1.x)
615:  	    && (RetrieveInfo(&temp).location.y == objects_array[i].corner1.y))
616:  	   || ((RetrieveInfo(&temp).location.x == objects_array[i].corner2.x)
617:  	     &&(RetrieveInfo(&temp).location.y == objects_array[i].corner2.y)))
618:  	{
619:  	  /*if this corner found is the first of the two allready known
620:  	    corners, then set the first canidate coord data and reset data
621:  	    to find the next canidate corner point*/
622:  	   if(corner_count == 1)
623:  	   {
624:  	      canidate_coord1.x = temp_coord.x;
625:  	      canidate_coord1.y = temp_coord.y;
626:  	      canidate_dist1 = max_dist;
627:  
628:  	      corner_count = 2; /*set for next corner*/
629:  	      max_dist = 0.0;
630:  	   }
631:  	   else if(corner_count == 2)
632:  	   {
633:  	      /*the second canidate is always a corner*/
634:  	      all_objects[i].corner4.x = temp_coord.x;
635:  	      all_objects[i].corner4.y = temp_coord.y;
636:  	      
637:  	      max_dist = 0.0; /*set for next corner canidate*/
638:  	   }
639:  	}
640:  
641:  	/*calculate the distance between the current point being checked and
642:  	  each corner point*/
643:  	temp_dist1 = findDist(all_objects[i].corner1.x,
644:  			      all_objects[i].corner1.y,
645:  			      RetrieveInfo(&temp).location.x,
646:  			      RetrieveInfo(&temp).location.y);
647:  	temp_dist2 = findDist(all_objects[i].corner2.x,
648:  			      all_objects[i].corner2.y,
649:  			      RetrieveInfo(&temp).location.x,
650:  			      RetrieveInfo(&temp).location.y);
651:  
652:  	/*if the current point is the furthest away sofar, store this point
653:  	  untill it is overridden or becomes a canidate point*/
654:  	if((temp_dist1 + temp_dist2) > max_dist)
655:  	{
656:  	   temp_coord.x = RetrieveInfo(&temp).location.x;
657:  	   temp_coord.y = RetrieveInfo(&temp).location.y;
658:  	  
659:  	   max_dist = (temp_dist1 + temp_dist2);
660:  	}
661:  
662:  	Advance(&temp);
663:        }
664:  
665:        /*from the three canidate coords find the two real corners.*/
666:        /*the second canidate will always be a corner, must test 1 vs 3, where
667:  	three is in the variables temp_coord and max_dist.*/
668:        if(canidate_dist1 > max_dist) /*first canidate*/
669:        {
670:           all_objects[i].corner3.x = canidate_coord1.x;
671:  	 all_objects[i].corner3.y = canidate_coord1.y;
672:        }
673:        else /*third canidate*/
674:        {
675:           all_objects[i].corner3.x = temp_coord.x;
676:  	 all_objects[i].corner3.y = temp_coord.y;
677:        }
678:  /*printf("corner3: (%d, %d)  corner4: (%d, %d)\n", corner3[i].x,
679:    corner3[i].y, corner4[i].x, corner4[i].y);*/
680:     }
681:  }
682:  
683:  /*takes a pointer to an image, and a pointer pointing to an array of
684:    chain codes pointers, here each chainCode pointer needs to be accessed
685:    by calculating the memory address.*/
686:  void showBound(PGMImage *original, list_info* chainCodes, object *all_objects)
687:  {     
688:     int i;
689:  
690:     /*find the first two corners.  they will be across a diagnal.*/
691:     findFirstTwoCorners(all_objects, chainCodes);
692:     /*find the second two corners.  they will be across a diagnal too.*/
693:     findSecondTwoCorners(all_objects, chainCodes);
694:     /*     
695:     for(i = 0; chainCodes[i] && i < MAX_CHAINS; i++)
696:     {
697:        setCLines(corner1[i].x, corner1[i].y, corner3[i].x, corner3[i].y,yellow);
698:        setCLines(corner4[i].x, corner4[i].y, corner2[i].x, corner2[i].y,yellow);
699:        setCLines(corner3[i].x, corner3[i].y, corner2[i].x, corner2[i].y,yellow);
700:        setCLines(corner4[i].x, corner4[i].y, corner1[i].x, corner1[i].y,yellow);
701:        setCPixel(corner1[i].x, corner1[i].y, magenta);
702:        setCPixel(corner2[i].x, corner2[i].y, magenta);
703:        setCPixel(corner3[i].x, corner3[i].y, magenta);
704:        setCPixel(corner4[i].x, corner4[i].y, magenta);
705:        }*/
706:  }
707:  
708:  /*returns the number of objects found*/
709:  /*places the corner info in the global corners data*/
710:  int detect_corners(PGMImage* org_img, list_info* current_chaincodes,
711:  		   object *objects_array)
712:  {
713:     int i; /*temporarily holds number of chaincodes*/
714:  
715:     showBound(org_img, current_chaincodes, objects_array);
716:  
717:     /*when this stops, i holds the number of chaincodes*/
718:     for(i = 0; (i < MAX_CHAINS) && chain_codes && chain_codes[i].cur; i++);
719:  
720:     return i;
721:  }
722:  
723:  /*******Find the game board********************************************
724:  **********************************************************************/
725:  
726:  float compare_length(float length1, float length2)
727:  {
728:     float denom_check = fabs((length1 + length2) / 2);
729:     if(denom_check == 0)
730:     {
731:        /*the only way to possibly pull this off is if one point is
732:  	is considered more than one corner.  This is most likely
733:  	to happen where the chain was only two or three nodes long.*/
734:        /*Just set the error to the maximum value obtained from float.h,
735:  	since such a small chain is not what we are looking for.*/
736:       
737:       /*error24to13[i] =*/ return FLT_MAX;
738:     }
739:     else
740:  /*error24to13[i] =*/return fabs(length1 - length2) / denom_check;
741:  }
742:  
743:  /*determines which object is the game board and determine moves made in
744:     the game.*/
745:  /*takes the number of object corners (equal to # of chaincodes) as param.*/
746:  int detect_game(list_info *current_chaincodes, object *object_array)
747:  {
748:     float length2to4[MAX_CHAINS], length1to3[MAX_CHAINS]; /*side pairs*/
749:     float length1to4[MAX_CHAINS], length2to3[MAX_CHAINS]; /*side pairs*/
750:     float length1to2[MAX_CHAINS], length3to4[MAX_CHAINS]; /*diagnaols*/
751:  
752:     float error24to13[MAX_CHAINS], error14to23[MAX_CHAINS];/*opp. sides*/
753:     float error14to13[MAX_CHAINS], error23to24[MAX_CHAINS];/*share corner*/
754:     float error31to32[MAX_CHAINS], error41to42[MAX_CHAINS];/*share corner*/
755:     float error12to34[MAX_CHAINS];/*diagnaols*/
756:  
757:     float error_avg;/*average of the errors stored in error##to## variables*/
758:  
759:     int i, k; /*loop counting*/
760:     /*float denom_check; /*make sure denominators are not zero*/
761:     list* temp;
762:  
763:     /*the most likely object (0 to num_of_corners) that is to be considered
764:       as the board.  The float is the error associated with this object.*/
765:     int most_likely = -1;
766:     float ml_error = FLT_MAX; /*just to make sure*/
767:  
768:     /*for each chaincode*/
769:     for(i = 0; (i < MAX_CHAINS) && current_chaincodes &&
770:  	 current_chaincodes[i].cur; i++)
771:     {
772:        /*count the number of nodes in the chaincode.  Unless the size is
773:         considered long enough, skip it and move on.*/
774:        if(Length(¤t_chaincodes[i]) < DECENT_SIZED_OBJECT)
775:  	 continue;
776:  
777:        /*since points 1 & 2 are at a diagnal, and 3 & 4 are at a diagnol,
778:           then the dist between  2 and 4 and 1 and three should be close
779:           in value.*/
780:        length2to4[i] = findDist(object_array[i].corner2.x,
781:  			       object_array[i].corner2.y,
782:  			       object_array[i].corner4.x,
783:  			       object_array[i].corner4.y);
784:        length1to3[i] = findDist(object_array[i].corner1.x,
785:  			       object_array[i].corner1.y,
786:  			       object_array[i].corner3.x,
787:  			       object_array[i].corner3.y);
788:  
789:        /*the other side pair*/
790:        length1to4[i] = findDist(object_array[i].corner1.x,
791:  			       object_array[i].corner1.y,
792:  			       object_array[i].corner4.x,
793:  			       object_array[i].corner4.y);
794:        length2to3[i] = findDist(object_array[i].corner2.x,
795:  			       object_array[i].corner2.y,
796:  			       object_array[i].corner3.x,
797:  			       object_array[i].corner3.y);
798:        
799:        /*diagnols... always will be 1 & 2 and 3 & 4*/
800:        length1to2[i] = findDist(object_array[i].corner1.x,
801:  			       object_array[i].corner1.y,
802:  			       object_array[i].corner2.x,
803:  			       object_array[i].corner2.y);
804:        length3to4[i] = findDist(object_array[i].corner3.x,
805:  			       object_array[i].corner3.y,
806:  			       object_array[i].corner4.x,
807:  			       object_array[i].corner4.y);
808:  
809:        /*calculate percent errors for all edge (and diagnal) combinations*/
810:        error24to13[i] = compare_length(length2to4[i], length1to3[i]);/*op.side*/
811:        error14to23[i] = compare_length(length1to4[i], length2to3[i]);
812:        error14to13[i] = compare_length(length1to4[i], length1to3[i]);/*1 crn.*/
813:        error23to24[i] = compare_length(length2to3[i], length2to4[i]);
814:        error31to32[i] = compare_length(length1to3[i], length2to3[i]);
815:        error41to42[i] = compare_length(length1to4[i], length2to4[i]);
816:        error12to34[i] = compare_length(length1to2[i], length3to4[i]);/*diag.*/
817:  
818:        /*average all of the error values together*/
819:        error_avg = ((error24to13[i] + error14to23[i] + error14to13[i] +
820:  		    error23to24[i] + error31to32[i] + error41to42[i] +
821:  		    error12to34[i]) / 7);
822:        
823:  /*printf("error avg: %f\n\n",  error_avg);*/
824:        /*determine if the current object is considered the most likely to
825:           be the ttt board so far.  Average of all the error##to##'s.
826:           If the current is */
827:        if(ml_error > error_avg)
828:        {
829:           most_likely = i;
830:           ml_error = error_avg;
831:        }
832:     }
833:  
834:    printf("Object %d is most likely the board with %f\%% error.\n",
835:       most_likely, ml_error * 100);
836:  
837:    return most_likely; /*return the object number that is the board*/
838:  }
839:  
840:  coord search_moravec(coord point1, coord point2, PGMImage *moravec)
841:  {
842:     int i, j; /*loop counting*/
843:     float t;
844:     coord temp;
845:     float dist = findDist(point1.x, point1.y, point2.x, point2.y);
846:     int search_size = 20;
847:  
848:     coord most_likely_corner = {-1, -1};
849:     int most_likely_sum = 0;
850:     int sum_moravec_points = 0;
851:  
852:     /*calculate the coordinates where the divider edges should be.*/
853:     for(t = ONE_SIXTH; t < (.45); t += (1 / dist))
854:     {
855:        temp = find_dividers(point1, point2, t);
856:        search_size = findDist(point1.x, point1.y, point2.x, point2.y) / 10;
857:        sum_moravec_points = 0; /*clear this before next iteration*/      
858:  
859:        for(i = -(search_size); i <= search_size; i++)
860:        {
861:  	 for(j = -(search_size); j <= search_size; j++)
862:  	 {
863:  	    if(rgb_avg((*moravec).data[temp.y + i][temp.x + j]) > 115)
864:  	    {
865:  	      if(temp.x == 236 &&  temp.y == 296)
866:  setCPixel(temp.x + j, temp.y + i, red);
867:  	       sum_moravec_points++;
868:  	    }
869:  	 }
870:        }
871:  
872:        /*if the current point in the search is the best, store it*/
873:        if(most_likely_sum < sum_moravec_points)
874:        {
875:  	 most_likely_corner.x = temp.x;
876:  	 most_likely_corner.y = temp.y;
877:           most_likely_sum = sum_moravec_points;
878:  printf("temp: (%d, %d)\n", temp.x, temp.y);
879:  printf("m sum: %d\n", sum_moravec_points);
880:        }
881:     }
882:  printf("ml: (%d, %d)\n", most_likely_corner.x, most_likely_corner.y);
883:  printf("ml sum: %d\n\n", most_likely_sum);
884:  
885:     return most_likely_corner;
886:  }
887:  
888:  void detect_ttt_board(object board_object, ttt *board_details,
889:  		      PGMImage *moravec)
890:  {
891:     
892:     /*copy the corners into the ttt datatype*/
893:     memcpy(&(board_details->corners), &board_object, sizeof(object));
894:     
895:     /*   board_details->side13 = search_moravec(board_object.corner1,
896:  	board_object.corner3, moravec);*/
897:  
898:     board_details->side14 = search_moravec(board_object.corner1,
899:  					  board_object.corner4, moravec);
900:     board_details->side23 = search_moravec(board_object.corner2,
901:  					 board_object.corner3, moravec);
902:  
903:     /*   board_details->side24 = search_moravec(board_object.corner2,
904:  					 board_object.corner4, moravec);
905:     board_details->side31 = search_moravec(board_object.corner3,
906:     board_object.corner1, moravec);*/
907:  
908:     board_details->side32 = search_moravec(board_object.corner3,
909:  					  board_object.corner2, moravec);
910:     board_details->side41 = search_moravec(board_object.corner4,
911:  					 board_object.corner1, moravec);
912:  
913:     /*   board_details->side42 = search_moravec(board_object.corner4,
914:  	board_object.corner2, moravec);*/
915:  
916:     /*calculate the coordinates of the middle points to the board*/
917:     board_details->middle1 = find_dividers(board_object.corner1,
918:  					  board_object.corner2, ONE_THIRD);
919:     board_details->middle2 = find_dividers(board_object.corner2,
920:  					  board_object.corner1, ONE_THIRD);
921:     board_details->middle3 = find_dividers(board_object.corner3,
922:  					  board_object.corner4, ONE_THIRD);
923:     board_details->middle4 = find_dividers(board_object.corner4,
924:  					  board_object.corner3, ONE_THIRD);
925:  }
926:  
927:  /*****Find the pieces***************************************************
928:  ***********************************************************************/
929:  
930:  /*Takes four points that must be in order going around the region.  Also,
931:    needs a pointer to an image with the moravec filter applied.*/
932:  int search_area(coord point1, coord point2, coord point3, coord point4,
933:  		PGMImage *moravec)
934:  {
935:     /*The following variables are used to calculate the existence of a marble
936:       at a location on the board or not.  Uses the following basic formula
937:          new_point = ((t-1) * 1st point) + (t * 2nd point)
938:       where the start & end coords are opposite sides of the area being
939:       searched.  If you take the entire area of a single position on a ttt
940:       board, the area that is searched is the region that is bouned by the
941:       points halfway between those passed in to this function.*/
942:       
943:     float t, s, r; /*hold values incrementing from 0 to 1*/
944:     coord t_start, t_end, s_start, s_end;
945:     coord temp_t, temp_s, temp_r;
946:     float dist_t, dist_s, dist_r;
947:  
948:     /*pixel count of those over the threashhold and total pixels*/
949:     int pixel_count = 0, moravec_count = 0;
950:  
951:     t_start = find_dividers(point1, point2, ONE_HALF);
952:     t_end = find_dividers(point1, point4, ONE_HALF);
953:  
954:     s_start = find_dividers(point3, point2, ONE_HALF);
955:     s_end = find_dividers(point3, point4, ONE_HALF);
956:  
957:     dist_t = findDist(t_start.x, t_start.y, t_end.x, t_end.y);
958:     dist_s = findDist(s_start.x, s_start.y, s_end.x, s_end.y);
959:     printf("%f  %f\n", dist_t, dist_s);
960:  
961:     /*march two points along that parallel each other in the search area*/
962:     for(t = 0.0, s = 0.0; t <= 1.0; t += (1.0 / dist_t), s += (1.0 / dist_s))
963:     {
964:        temp_t = find_dividers(t_start, t_end, t);
965:  
966:  	 temp_s = find_dividers(s_start, s_end, s);
967:  
968:  	 dist_r = findDist(temp_t.x, temp_t.y, temp_s.x, temp_s.y);
969:  
970:           /*march a single point along that starts at temp_s and ends
971:  	   at temp_t.  This fills in the region for the search.*/
972:  	 for(r = 0.0; r <= 1.0; r += (1 / dist_r))
973:  	 {
974:  	    temp_r = find_dividers(temp_s, temp_t, r);
975:  
976:              /*talley the number of edge points found (rgb. avg. >= 128)*/
977:  	    if(rgb_avg((*moravec).data[temp_r.y][temp_r.x]) >= 128)
978:  	    {
979:  	       setCPixel(temp_r.x, temp_r.y, blue);
980:  	       moravec_count++;
981:  	    }
982:  	    pixel_count++; /*increment the number of pixels*/
983:  	 }
984:  
985:     }
986:  
987:     printf("total: %d  edge: %d\n", pixel_count, moravec_count);
988:     
989:     /*if the number of edge pixels is greater than 10% of the total, then
990:       this is an edge of marble.  The percent is there because there could be
991:       a few small blobs that get counted when the shouldn't that otherwise
992:       would give a positive hit that a marble was found*/
993:     if(moravec_count >= (pixel_count / 10))
994:        return TRUE;
995:  
996:     /*otherwise a marble isn't here*/
997:     return FALSE;
998:  }
999:  
1000:  /*takes the object number that is determined to be the board*/
1001:  void detect_pieces(ttt *board_data, PGMImage* moravec)
1002:  {
1003:      if(search_area(board_data->corners.corner1, board_data->side13,
1004:  		 board_data->middle1, board_data->side14, moravec))
1005:      printf("corner 1 has a piece\n");
1006:    else
1007:      printf("corner 1 has no piece\n");
1008:    
1009:    
1010:    if(search_area(board_data->middle1, board_data->middle3,
1011:  		 board_data->middle2, board_data->middle4, moravec))
1012:      printf("middle has a piece\n");
1013:    else
1014:    printf("middle has no piece\n");
1015:    
1016:  
1017:    if(search_area(board_data->corners.corner2, board_data->side23,
1018:  		 board_data->middle2, board_data->side24, moravec))
1019:      printf("corner 2 has a piece\n");
1020:    else
1021:      printf("corner 2 has no piece\n");
1022:  
1023:  setCPixel(board_data->middle1.x, board_data->middle1.y, red);
1024:  setCPixel(board_data->middle2.x, board_data->middle2.y, green);
1025:  setCPixel(board_data->middle3.x, board_data->middle3.y, blue);
1026:  setCPixel(board_data->middle4.x, board_data->middle4.y, yellow);
1027:  }
1028:  
1029:  
1030:  /* =================================================================
1031:   * Callback functions.
1032:   *
1033:   * color = displayed graphics in window
1034:   * menu = menu event handling
1035:   * keyboard = deyboard event handling
1036:   * ----------------------------------------------------------------- */
1037:  void color(void)
1038:  {
1039:    /*glClear (GL_COLOR_BUFFER_BIT);*/
1040:  
1041:    /*show the current image*/
1042:    showColor(img_cur);
1043:  
1044:    /*show the current abstract information*/
1045:    showAbstract(all_objects, ttt_board);   
1046:  
1047:  }
1048:  
1049:  void buffer()
1050:  {
1051:     /*if the drawing state of all objects is enabled, recalculate*/
1052:     if(draw_abstract_lines >= 0)
1053:        draw_abstract_lines = detect_corners(img_cur, chain_codes, all_objects);
1054:  
1055:     /*if the drawing state of the object most likely to be the board is
1056:       found, recalulate*/
1057:     if(draw_abstract_board >= 0)
1058:        draw_abstract_board = detect_game(chain_codes, all_objects);
1059:  
1060:     glutPostRedisplay();
1061:  }
1062:  
1063:  #define RESTART 0
1064:  #define PERS_CORR 3
1065:  #define COLOR_TO_GRAY 4
1066:  #define MORAVEC 5
1067:  #define EDGES 6
1068:  #define CORNERS 7
1069:  #define BUFFERS 8
1070:  #define GAME 9
1071:  #define PIECES 10
1072:  
1073:  /*this is not a callback function, but is used inside menu() for setting
1074:    new states of execution*/
1075:  void reset_state(PGMImage* new_current)
1076:  {
1077:     img_cur = new_current;
1078:     draw_abstract_lines = -1;
1079:     draw_abstract_board = -1;
1080:     free_chaincodes(&chain_codes);
1081:  }
1082:  
1083:  void menu(int selection)
1084:  {
1085:     if(selection == RESTART)   
1086:     {
1087:        reset_state(img_original);
1088:     }
1089:     if(selection == PERS_CORR)
1090:     {
1091:        pers_corr(img_pers_corr, img_cur);
1092:        reset_state(img_pers_corr);
1093:     }
1094:     if(selection == COLOR_TO_GRAY)
1095:     {
1096:        color_to_gray(img_grayscale, img_cur);
1097:        reset_state(img_grayscale);
1098:     }
1099:     if(selection == MORAVEC)
1100:     {
1101:        moravec(img_moravec, img_cur);
1102:        reset_state(img_moravec);
1103:     }
1104:     if(selection == EDGES)
1105:     {
1106:        /*if there is a chaincode already in memory, free it before a new one
1107:  	is created.*/
1108:        showChain(img_cur, &chain_codes);
1109:     }
1110:     if(selection == CORNERS)
1111:     {
1112:        /*if there are chaincodes already in memory, free it before a new one
1113:  	is created.*/
1114:        showChain(img_cur, &chain_codes);
1115:  
1116:        draw_abstract_lines = detect_corners(img_cur, chain_codes, all_objects);
1117:     }
1118:     if(selection == BUFFERS)
1119:     {
1120:        if(is_buffered == FALSE)
1121:        {
1122:  	 glutIdleFunc(buffer);
1123:  	 is_buffered = TRUE;
1124:        }
1125:        else
1126:        {
1127:  	 glutIdleFunc(NULL);
1128:  	 is_buffered = FALSE;
1129:        }
1130:     }
1131:     if(selection == GAME)
1132:     {
1133:        /*need an image with the moravec filter applied*/
1134:        moravec(img_moravec, img_cur);
1135:  
1136:        /*if there are chaincodes already in memory, free it before a new one
1137:  	is created.*/
1138:        showChain(img_cur, &chain_codes);
1139:  
1140:        detect_corners(img_cur, chain_codes, all_objects);
1141:        draw_abstract_board = detect_game(chain_codes, all_objects);
1142:        detect_ttt_board(all_objects[draw_abstract_board], ttt_board,
1143:  		       img_moravec);
1144:  return;
1145:     }
1146:     if(selection == PIECES)
1147:     {
1148:        /*need an image with the moravec filter applied*/
1149:        moravec(img_moravec, img_cur);
1150:  
1151:        /*if there are chaincodes already in memory, they are freed before a
1152:  	new one is created.*/
1153:        showChain(img_cur, &chain_codes);
1154:  
1155:        /*detect the ttt game board*/
1156:        detect_corners(img_cur, chain_codes, all_objects);
1157:        draw_abstract_board = detect_game(chain_codes, all_objects);
1158:        detect_ttt_board(all_objects[draw_abstract_board], ttt_board,
1159:  		       img_moravec);
1160:  
1161:        /*detect the locations of the pieces*/
1162:        detect_pieces(ttt_board, img_moravec);
1163:  
1164:        glFlush();
1165:        return;
1166:     }
1167:  
1168:     glutPostRedisplay(); /*redraw the image*/
1169:  }
1170:  
1171:  void keyboard(unsigned char key, int x, int y)
1172:  { 
1173:     switch (key)
1174:     {
1175:        case 27: 
1176:           exit(0);
1177:           break;
1178:     }
1179:  }
1180:  
1181:  void mouse(int button, int state, int x, int y)
1182:  {
1183:    char temp[50];
1184:    
1185:    if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
1186:    {
1187:       sprintf(temp, "(x, y): (%d, %d)  red: %d green: %d blue: %d\n",
1188:  	     x, VSIZE - y, (*img_cur).data[VSIZE - y][x].red,
1189:  	     (*img_cur).data[VSIZE - y][x].green,
1190:  	     (*img_cur).data[VSIZE - y][x].blue);
1191:       setCRect(0, 0, 200, 12, black);
1192:       glColor3f(1.0, 0.0, 0.0);
1193:       drawString(0, 0, GLUT_BITMAP_TIMES_ROMAN_10, temp);
1194:       glFlush();
1195:    }
1196:  }
1197:  
1198:    
1199:  /* =================================================================
1200:   * init() & parse_command_line()
1201:   * initalize none-OpenGL related things.
1202:   * ----------------------------------------------------------------- */
1203:  
1204:  /*set global flag variables from things specified at commandline.*/
1205:  /*return the filename specified, if none specified exit().*/
1206:  char* parse_command_line(int argc, char** argv)
1207:  {
1208:     /*parse the command line*/
1209:     if(argc == 1)
1210:     {
1211:        printf("To few parameters.\n");
1212:        printf("Usage: research <file.pgm>\n");
1213:        exit(1);
1214:     }
1215:     else if(argc == 2)
1216:     {
1217:       return argv[1];
1218:     }
1219:     else
1220:     {
1221:        printf("To many parameters.\n");
1222:        printf("Usage: research <file.pgm>\n");
1223:        exit(1);
1224:     }
1225:  }
1226:  
1227:  char* init (int argc, char** argv)
1228:  {
1229:     char* PGMfileName;/*pointer to the string containing the filename*/
1230:  
1231:     /*parse the command line*/
1232:     PGMfileName = parse_command_line(argc, argv);
1233:     
1234:  /*
1235:   * Read in image file. - note: sets our global values, too.
1236:   * ----------------------------------------------------------------- */
1237:        
1238:     /*allocate memory for original image*/
1239:     img_original = (PGMImage*) malloc(sizeof(PGMImage));
1240:     getPGMfile(PGMfileName, img_original);
1241:     HSIZE = (*img_original).width;
1242:     VSIZE = (*img_original).height;
1243:     MVAL = (*img_original).maxVal;
1244:  
1245:     img_cur = img_original; /*VERY IMPORTANT to set this*/
1246:              
1247:     /*allocate memory for pers. corr. image*/
1248:     img_pers_corr = (PGMImage*) malloc(sizeof(PGMImage));
1249:     (*img_pers_corr).width = HSIZE;
1250:     (*img_pers_corr).height = VSIZE;
1251:     (*img_pers_corr).maxVal = 255;
1252:               
1253:     img_grayscale = (PGMImage*) malloc(sizeof(PGMImage));
1254:     (*img_grayscale).width = HSIZE;
1255:     (*img_grayscale).height = VSIZE;
1256:     (*img_grayscale).maxVal = 255;
1257:  
1258:     img_moravec = (PGMImage*) malloc(sizeof(PGMImage));
1259:     (*img_moravec).width = HSIZE;
1260:     (*img_moravec).height = VSIZE;
1261:     (*img_moravec).maxVal = 255;
1262:  
1263:  
1264:     all_objects = (object*) malloc(sizeof(object) * MAX_CHAINS);
1265:     memset(all_objects, 0, sizeof(object) * MAX_CHAINS);
1266:     
1267:     ttt_board = (ttt*) malloc(sizeof(ttt));
1268:     memset(ttt_board, 0, sizeof(ttt));
1269:  
1270:     return PGMfileName;
1271:  }
1272:  
1273:  
1274:  int main(int argc, char** argv)
1275:  {
1276:     char* PGMfileName;
1277:     int WindowID; /*unique window id, there is only one used*/
1278:     int i;  /*looping variable*/
1279:  
1280:  /*  
1281:   * Call our init function to define non-OpenGL related things.
1282:   * Have init return a pointer to the filename, used to display the
1283:   * filename in the titlebar of the window.
1284:   * ----------------------------------------------------------------- */
1285:     PGMfileName = init (argc, argv);
1286:  
1287:  /*
1288:   * Initialize the glut package.
1289:   * ----------------------------------------------------------------- */
1290:     glutInit(&argc, argv);
1291:     glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
1292:  
1293:  /*           
1294:   * Define a new window (its size, position and title).
1295:   * ----------------------------------------------------------------- */
1296:     glutInitWindowSize (HSIZE, VSIZE);  /*size*/
1297:     glutInitWindowPosition (10, 10);    /*position*/
1298:     WindowID = glutCreateWindow (PGMfileName); /*title*/
1299:     glutSetWindow(WindowID);
1300:     glutDisplayFunc(color);
1301:        
1302:  /*
1303:   * select clearing color - white
1304:   */
1305:     glClearColor (1.0, 1.0, 1.0, 0.0);
1306:  
1307:  /*
1308:   * initialize viewport values
1309:   */
1310:     glMatrixMode(GL_PROJECTION);
1311:     glLoadIdentity();
1312:     glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
1313:  
1314:     /*add menus*/
1315:     glutCreateMenu(menu);
1316:     glutAddMenuEntry("Restart", RESTART);
1317:     glutAddMenuEntry("perspective correction", PERS_CORR);
1318:     glutAddMenuEntry("color to gray", COLOR_TO_GRAY);
1319:     glutAddMenuEntry("moravec", MORAVEC);
1320:     glutAddMenuEntry("edges", EDGES);
1321:     glutAddMenuEntry("corners", CORNERS);
1322:     glutAddMenuEntry("detect game", GAME);
1323:     glutAddMenuEntry("detect pieces", PIECES);
1324:     glutAddMenuEntry("toggle buffering", BUFFERS);
1325:     glutAttachMenu(GLUT_RIGHT_BUTTON);
1326:  
1327:     glutMouseFunc(mouse);
1328:     glutKeyboardFunc(keyboard); 
1329:     glutMainLoop();
1330:  
1331:  /*  
1332:   * When we reach here, we've left the event loop and are ready to
1333:   * exit.
1334:   * ----------------------------------------------------------------- */
1335:     return 0;
1336:  }
1337: