circlefit.c
上传用户:xk288cn
上传日期:2007-05-28
资源大小:4876k
文件大小:7k
源码类别:

GIS编程

开发平台:

Visual C++

  1. /* Copyright (c) Mark J. Kilgard, 1997. */
  2. /* This program is freely distributable without licensing fees 
  3.    and is provided without guarantee or warrantee expressed or 
  4.    implied. This program is -not- in the public domain. */
  5. /* This is a small interactive demo of Dave Eberly's algorithm
  6.    that fits a circle boundary to a set of 2D points. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <math.h>
  10. #include <GL/glut.h>
  11. /* Some <math.h> files do not define M_PI... */
  12. #ifndef M_PI
  13. #define M_PI 3.14159265358979323846
  14. #endif
  15. typedef struct {
  16.   double x, y;
  17. } Point2;
  18. /****************************************************************************
  19.    Least squares fit of circle to set of points.
  20.    by Dave Eberly (eberly@cs.unc.edu or eberly@ndl.com)
  21.    ftp://ftp.cs.unc.edu/pub/users/eberly/magic/circfit.c
  22.   ---------------------------------------------------------------------------
  23.    Input:  (x_i,y_i), 1 <= i <= N, where N >= 3 and not all points
  24.            are collinear
  25.    Output:  circle center (a,b) and radius r
  26.   
  27.    Energy function to be minimized is
  28.   
  29.       E(a,b,r) = sum_{i=1}^N (L_i-r)^2
  30.   
  31.    where L_i = |(x_i-a,y_i-b)|, the length of the specified vector.
  32.    Taking partial derivatives and setting equal to zero yield the
  33.    three nonlinear equations
  34.   
  35.    E_r = 0:  r = Average(L_i)
  36.    E_a = 0:  a = Average(x_i) + r * Average(dL_i/da)
  37.    E_b = 0:  b = Average(y_i) + r * Average(dL_i/db)
  38.   
  39.    Replacing r in the last two equations yields
  40.   
  41.      a = Average(x_i) + Average(L_i) * Average(dL_i/da) = F(a,b)
  42.      b = Average(y_i) + Average(L_i) * Average(dL_i/db) = G(a,b)
  43.   
  44.    which can possibly be solved by fixed point iteration as
  45.   
  46.      a_{n+1} = F(a_n,b_n),  b_{n+a} = G(a_n,b_n)
  47.   
  48.    with initial guess a_0 = Average(x_i) and b_0 = Average(y_i).
  49.    Derivative calculations show that
  50.   
  51.      dL_i/da = (a-x_i)/L_i,  dL_i/db = (b-y_i)/L_i.
  52.   
  53.   ---------------------------------------------------------------------------
  54.    WARNING.  I have not analyzed the convergence properties of the fixed
  55.    point iteration scheme.  In a few experiments it seems to converge
  56.    just fine, but I do not guarantee convergence in all cases.
  57.  ****************************************************************************/
  58. int
  59. CircleFit(int N, Point2 * P, double *pa, double *pb, double *pr)
  60. {
  61.   /* user-selected parameters */
  62.   const int maxIterations = 256;
  63.   const double tolerance = 1e-06;
  64.   double a, b, r;
  65.   /* compute the average of the data points */
  66.   int i, j;
  67.   double xAvr = 0.0;
  68.   double yAvr = 0.0;
  69.   for (i = 0; i < N; i++) {
  70.     xAvr += P[i].x;
  71.     yAvr += P[i].y;
  72.   }
  73.   xAvr /= N;
  74.   yAvr /= N;
  75.   /* initial guess */
  76.   a = xAvr;
  77.   b = yAvr;
  78.   for (j = 0; j < maxIterations; j++) {
  79.     /* update the iterates */
  80.     double a0 = a;
  81.     double b0 = b;
  82.     /* compute average L, dL/da, dL/db */
  83.     double LAvr = 0.0;
  84.     double LaAvr = 0.0;
  85.     double LbAvr = 0.0;
  86.     for (i = 0; i < N; i++) {
  87.       double dx = P[i].x - a;
  88.       double dy = P[i].y - b;
  89.       double L = sqrt(dx * dx + dy * dy);
  90.       if (fabs(L) > tolerance) {
  91.         LAvr += L;
  92.         LaAvr -= dx / L;
  93.         LbAvr -= dy / L;
  94.       }
  95.     }
  96.     LAvr /= N;
  97.     LaAvr /= N;
  98.     LbAvr /= N;
  99.     a = xAvr + LAvr * LaAvr;
  100.     b = yAvr + LAvr * LbAvr;
  101.     r = LAvr;
  102.     if (fabs(a - a0) <= tolerance && fabs(b - b0) <= tolerance)
  103.       break;
  104.   }
  105.   *pa = a;
  106.   *pb = b;
  107.   *pr = r;
  108.   return (j < maxIterations ? j : -1);
  109. }
  110. enum {
  111.   M_SHOW_CIRCLE, M_CIRCLE_INFO, M_RESET_POINTS, M_QUIT
  112. };
  113. #define MAX_POINTS 100
  114. int num = 0;
  115. Point2 list[MAX_POINTS];
  116. int circleFitNeedsRecalc = 0;
  117. int showCircle = 1;
  118. int circleInfo = 0;
  119. int windowHeight;
  120. double a, b, r = 0.0;   /* X, Y, and radius of best fit circle. 
  121.                          */
  122. void
  123. drawCircle(float x, float y, float r)
  124. {
  125.   double angle;
  126.   glPushMatrix();
  127.   glTranslatef(x, y, 0);
  128.   glBegin(GL_TRIANGLE_FAN);
  129.   glVertex2f(0, 0);
  130.   for (angle = 0.0; angle <= 2 * M_PI; angle += M_PI / 24) {
  131.     glVertex2f(r * cos(angle), r * sin(angle));
  132.   }
  133.   glEnd();
  134.   glPopMatrix();
  135. }
  136. void
  137. display(void)
  138. {
  139.   int i;
  140.   if (circleFitNeedsRecalc) {
  141.     int rc;
  142.     rc = CircleFit(num, list, &a, &b, &r);
  143.     if (rc == -1) {
  144.       fprintf(stderr, "circlefit: Problem fitting points to a circle encountered.n");
  145.     } else {
  146.       if (circleInfo) {
  147.         printf("%g @ (%g,%g)n", r, a, b);
  148.       }
  149.     }
  150.     circleFitNeedsRecalc = 0;
  151.   }
  152.   glClear(GL_COLOR_BUFFER_BIT);
  153.   if (showCircle && r > 0.0) {
  154.     glColor3ub(0xbb, 0xbb, 0xdd);
  155.     drawCircle(a, b, r);
  156.   }
  157.   glColor3ub(0, 100, 0);
  158.   glBegin(GL_POINTS);
  159.   for (i = 0; i < num; i++) {
  160.     glVertex2d(list[i].x, list[i].y);
  161.   }
  162.   glEnd();
  163.   glutSwapBuffers();
  164. }
  165. void
  166. reshape(int w, int h)
  167. {
  168.   glViewport(0, 0, w, h);
  169.   glMatrixMode(GL_PROJECTION);
  170.   glLoadIdentity();
  171.   gluOrtho2D(0, w, 0, h);
  172.   glMatrixMode(GL_MODELVIEW);
  173.   windowHeight = h;
  174. }
  175. void
  176. addPoint(double x, double y)
  177. {
  178.   if (num + 1 >= MAX_POINTS) {
  179.     fprintf(stderr, "circlefit: limited to only %d pointsn", MAX_POINTS);
  180.     return;
  181.   }
  182.   list[num].x = x;
  183.   list[num].y = y;
  184.   num++;
  185.   circleFitNeedsRecalc = 1;
  186.   glutPostRedisplay();
  187. }
  188. void
  189. mouse(int button, int state, int x, int y)
  190. {
  191.   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
  192.     addPoint(x, windowHeight - y);
  193.   }
  194. }
  195. void
  196. menu(int value)
  197. {
  198.   switch (value) {
  199.   case M_SHOW_CIRCLE:
  200.     showCircle = !showCircle;
  201.     break;
  202.   case M_CIRCLE_INFO:
  203.     circleInfo = !circleInfo;
  204.     break;
  205.   case M_RESET_POINTS:
  206.     num = 0;
  207.     r = 0.0;
  208.     break;
  209.   case M_QUIT:
  210.     exit(0);
  211.   }
  212.   glutPostRedisplay();
  213. }
  214. int
  215. main(int argc, char **argv)
  216. {
  217.   glutInitWindowSize(400, 400);
  218.   glutInit(&argc, argv);
  219.   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  220.   glutCreateWindow("Least squares fit of circle to set of points");
  221.   printf("n");
  222.   printf("Least squares fit of circle to set of pointsn");
  223.   printf("--------------------------------------------n");
  224.   printf("Click left mouse button to position points.  Then");
  225.   printf("program then shows the circle whose boundary bestn");
  226.   printf("fits the set of points specified.  Try clickingn");
  227.   printf("points in a near circle.n");
  228.   printf("n");
  229.   glClearColor(125.0 / 256.0, 158.0 / 256.0, 192.0 / 256.0, 1.0);
  230.   glPointSize(3.0);
  231.   glutReshapeFunc(reshape);
  232.   glutMouseFunc(mouse);
  233.   glutDisplayFunc(display);
  234.   glutCreateMenu(menu);
  235.   glutAddMenuEntry("Show/hide circle", M_SHOW_CIRCLE);
  236.   glutAddMenuEntry("Toggle info printing", M_CIRCLE_INFO);
  237.   glutAddMenuEntry("Reset points", M_RESET_POINTS);
  238.   glutAddMenuEntry("Quit", M_QUIT);
  239.   glutAttachMenu(GLUT_RIGHT_BUTTON);
  240.   glutMainLoop();
  241.   return 0;             /* ANSI C requires main to return int. */
  242. }