locked
Draw a Circle in vb.net or c#.net? RRS feed

  • Question

  • Hi All Respected,
    I am doing a graphical work, I have a bitmap which is plain canvas, user will choose three desire point and the application will draw a circle if will possible between the three point.
    1) vb or c#.net according my search, don't have any circle method except ellips....
    2) how will i draw circle between those three point?
    3) any api which i will call with P/Invoke approach.
    Please any one who has idea or suggestion please guide me.
    Thanks
    Bye
    Regards Rajat.
    Rajat Solanki makes I M P.
    Friday, April 10, 2009 8:03 AM

Answers

  • This is Pascal code from a CAD program I wrote several life-times ago.  You should have no trouble translating the algorithm:

    procedure fit3 (var p1, p2, p3 : dpoint);
    {Fits a circlular arc through points <p1, p2 and p3>. Data is stored in
     record <p1>}
    var x1, x2, x3, y1, y2, y3, cx, cy, y, d : real;
        rotcw : boolean;

    begin
      x1 := p1.co.x; x2 := p2.co.x; x3 := p3.co.x;
      y1 := p1.co.y; y2 := p2.co.y; y3 := p3.co.y;
      d  := (y2-y1)*(x3-x1) - (y3-y1)*(x2-x1);
      if d = 0 then           {Straight line}
        begin
          p1.ccur := line;
        end
      else if y1 = y2 then    {Special case}
        begin
          cx := (x1+x2)/2;
          cy := (y3+y1+(x3-x1)*(x3-x2)/(y3-y1))/2;
          p1.ccur := arcseg;
        end
      else
        begin                 {Normal case}
          cx := (y2-y1)*(y3*y3+x3*x3)+(y3-y2)*(y1*y1+x1*x1)+(y1-y3)*(y2*y2+x2*x2);
          cy := (x1-x2)*(y3*y3+x3*x3)+(x2-x3)*(y1*y1+x1*x1)+(x3-x1)*(y2*y2+x2*x2);
          cx := cx / 2.0 / d;
          cy := cy / 2.0 / d;
          p1.ccur := arcseg;
        end;

      if p1.ccur = arcseg then
        begin
          p1.center.x := cx;
          p1.center.y := cy;
          p1.radius   := sqrt (sqr (cx-x1) + sqr (cy-y1));
        end;
      if (p1.radius < MAXRADIUS) and (p1.ccur = arcseg) then
        begin
          {Compute rotation direction}
          rotcw := true;
          if x1 = x2 then
            begin
              if y2 < y1 then rotcw := false;
              if x3 < x1 then rotcw := not rotcw;
            end
            else
            begin
              y := ((y2-y1)*x3+y1*x2-y2*x1)/(x2-x1);
              if x2 < x1 then rotcw := false;
              if y3 > y  then rotcw := not rotcw;
            end;
          if rotcw then p1.dir := CW else p1.dir := CCW;

          {Compute starting and ending tangents}
          p1.sang := comparctang (p1, p1.co);
          p1.eang := comparctang (p1, p2.co);
        end
        else
        begin
          p1.ccur := line;
          p1.sang := complinetang (p1.co, p2.co);
          p1.eang := p1.sang;
        end;
    end;

    Hans Passant.
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Saturday, April 11, 2009 3:57 PM
  • I needed something to do also.  This uses gaussian elimination of a N X N matrix to fit three points to a circle.  The points are supplied in a point array and the bounding rectangle of the circle is returned.

    Private Function GetCircleRectFromPoints(ByVal Pts() As Point) As RectangleF
        '(X - Cx)^2 + (Y - Cy)^2 = R^2
        '(R^2 - Cx^2 - Cy^2) + 2X*Cx + 2Y*Cy = X^2 +  Y^2
        'Cr + A*X + B*Y = X^2 + Y^2
        'Solve matrix using gaussian elimination.
        Dim N As Integer = Pts.Length - 1
        Dim X(N, N) As Double, Y(N) As Double
        For I As Integer = 0 To N
          X(I, 0) = 1 : X(I, 1) = Pts(I).X : X(I, 2) = Pts(I).Y
          Y(I) = X(I, 1) * X(I, 1) + X(I, 2) * X(I, 2)
        Next
        Dim MatInv As New Elimination
        MatInv.ComputeCoefficents(X, Y)
        Dim A As Single = CSng(Y(1) / 2)
        Dim B As Single = CSng(Y(2) / 2)
        Dim R As Single = CSng(Math.Sqrt(Y(0) + A * A + B * B))
        Return New RectangleF(A - R, B - R, 2 * R, 2 * R)
      End Function
    
      Public Class Elimination
        Sub ComputeCoefficents(ByVal X(,) As Double, ByVal Y() As Double)
          Dim I, J, K, K1, N As Integer
          N = Y.Length
          For K = 0 To N - 1
            K1 = K + 1
            For I = K To N - 1
              If X(I, K) <> 0 Then
                For J = K1 To N - 1
                  X(I, J) /= X(I, K)
                Next J
                Y(I) /= X(I, K)
              End If
            Next I
            For I = K1 To N - 1
              If X(I, K) <> 0 Then
                For J = K1 To N - 1
                  X(I, J) -= X(K, J)
                Next J
                Y(I) -= Y(K)
              End If
            Next I
          Next K
          For I = N - 2 To 0 Step -1
            For J = N - 1 To I + 1 Step -1
              Y(I) -= X(I, J) * Y(J)
            Next J
          Next I
        End Sub
      End Class
    
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Saturday, April 11, 2009 8:15 PM
  • So anyway if you're for some reason not happy with nobugz' or JohnWein's solution, you can find the math for calculating the coordinates of the circumcenter here:

    http://en.wikipedia.org/wiki/Circumcircle#Cartesian_coordinates

    (Ax, Ay, Bx, By, Cx, Cy are the coordinates of your three points, D is just a temporary variable and the formulas give you x and y coordinates of the center of the circle you're looking for.) I would recommend translating point A to the origin of the coordinate system just like the article suggests. This simplifies the formula a lot. You'll also need the radius of the circle but this is easy to find, you only need to calculate the distance of the center to any of the three points. Keep in mind than if D = 0, the points are collinear and therefore there is no circle going through all of them. (Unless you consider straight line a special case of circle with infinite radius.) Here's an implementation I came up with:
        Private Sub DrawCircle(ByVal a As PointF, ByVal b As PointF, ByVal c As PointF, ByVal g As Graphics)
            g.TranslateTransform(a.X, a.Y)
            b.X -= a.X
            b.Y -= a.Y
            c.X -= a.X
            c.Y -= a.Y
            a.X = 0
            a.Y = 0
            Dim d As Single = 2 * (b.X * c.Y - b.Y * c.X)
            If d = 0 Then
                ' Points a, b, c are collinear
                g.DrawLine(Pens.Black, a.X, a.Y, b.X, b.Y)
                g.DrawLine(Pens.Black, b.X, b.Y, c.X, c.Y)
                g.DrawLine(Pens.Black, c.X, c.Y, a.X, a.Y)
            Else
                Dim center As New PointF((c.Y * (b.X * b.X + b.Y * b.Y) - b.Y * (c.X * c.X + c.Y * c.Y)) / d, (b.X * (c.X * c.X + c.Y * c.Y) - c.X * (b.X * b.X + b.Y * b.Y)) / d)
                Dim radius As Single = CSng(Math.Sqrt(center.X * center.X + center.Y * center.Y))
                g.FillEllipse(Brushes.Blue, center.X - 2, center.Y - 2, 4, 4)
                g.DrawEllipse(Pens.Black, center.X - radius, center.Y - radius, 2 * radius, 2 * radius)
            End If
            g.FillEllipse(Brushes.Red, a.X - 2, a.Y - 2, 4, 4)
            g.FillEllipse(Brushes.Red, b.X - 2, b.Y - 2, 4, 4)
            g.FillEllipse(Brushes.Red, c.X - 2, c.Y - 2, 4, 4)
            g.ResetTransform()
        End Sub
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Monday, April 13, 2009 1:04 PM

All replies

  • By drawing circle is kind of ellips... just set same width and height parameters and you got circle. Like:

    Graphics graphics = this.CreateGraphics();
    
    float x = ..;// x point where use click
    float y = ..;// y point where use click
    float circleSize = 10;// required circle size graphics.DrawEllipse(new Pen(Brushes.DeepSkyBlue), x, y, circleSize, circleSize);

     


    Regards
    Friday, April 10, 2009 8:19 AM
  • One more example - to draw circle when user click on form you could use the following code:

    public Form1()
            {
                InitializeComponent();
    
                this.Click += new EventHandler(Form1_Click);
            }
    
            void Form1_Click(object sender, EventArgs e)
            {
                MouseEventArgs clickArgs = (MouseEventArgs)e;
                Graphics graphics = this.CreateGraphics();
    
                this.Refresh();
    
                float circlesize = 20;
    
                float x = clickArgs.X - circlesize / 2;
                float y = clickArgs.Y - circlesize / 2;
    
                graphics.DrawEllipse(new Pen(Brushes.DeepSkyBlue), x, y, circlesize, circlesize);
            }

    Regards
    Friday, April 10, 2009 8:41 AM
  • You'll have to do the math to compute the extends of the circle.  Beware that two circles fit 3 points, a small one and a large one.  Also beware that it is quite unpredictable to the user what circle she'll get.  CAD programs typically let you pick two points, then show a rubber-band circle (moves with the mouse) to give a preview of the final circle.
    Hans Passant.
    Friday, April 10, 2009 9:06 AM
  • Hi All,
    Thanks for all to reply soon, but I have tried Ellisp.
    Actually Ellips first draw a rectangle, than draw a circle in the rectangle.
    but I want to draw to use of the three points in boundary of circle.
    may it possible to draw it?
    Please guide me.
    Thanks
    Bye
    Regards Rajat
    Rajat Solanki makes I M P.
    Friday, April 10, 2009 11:20 AM
  • Solve the three equations containing (X0,Y0), (X1,Y1) and (X2,Y2) for the circle's center and radius and supply the rectangles described to DrawEllipse.  Good homework.
    Friday, April 10, 2009 11:37 AM
  • Hi John Wein,
    Thanks for good work, but I am not expert as well as you, tell me how can solve it all, till draw a circle.
    I am waiting.
    Thanks
    Bye
    Rajat
    Rajat Solanki makes I M P.
    Friday, April 10, 2009 1:43 PM
  • The equation  of a circle is:

    (X - Xc)*2 + (Y - Yc)*2 = R*2

    When you substitute your three points for X and Y, you'll have three equations which can be solved for Xc, Yc and R.  From these points, you can compute the bounding rectangle for the DrawEllipse method.
    Friday, April 10, 2009 2:46 PM
  • This is Pascal code from a CAD program I wrote several life-times ago.  You should have no trouble translating the algorithm:

    procedure fit3 (var p1, p2, p3 : dpoint);
    {Fits a circlular arc through points <p1, p2 and p3>. Data is stored in
     record <p1>}
    var x1, x2, x3, y1, y2, y3, cx, cy, y, d : real;
        rotcw : boolean;

    begin
      x1 := p1.co.x; x2 := p2.co.x; x3 := p3.co.x;
      y1 := p1.co.y; y2 := p2.co.y; y3 := p3.co.y;
      d  := (y2-y1)*(x3-x1) - (y3-y1)*(x2-x1);
      if d = 0 then           {Straight line}
        begin
          p1.ccur := line;
        end
      else if y1 = y2 then    {Special case}
        begin
          cx := (x1+x2)/2;
          cy := (y3+y1+(x3-x1)*(x3-x2)/(y3-y1))/2;
          p1.ccur := arcseg;
        end
      else
        begin                 {Normal case}
          cx := (y2-y1)*(y3*y3+x3*x3)+(y3-y2)*(y1*y1+x1*x1)+(y1-y3)*(y2*y2+x2*x2);
          cy := (x1-x2)*(y3*y3+x3*x3)+(x2-x3)*(y1*y1+x1*x1)+(x3-x1)*(y2*y2+x2*x2);
          cx := cx / 2.0 / d;
          cy := cy / 2.0 / d;
          p1.ccur := arcseg;
        end;

      if p1.ccur = arcseg then
        begin
          p1.center.x := cx;
          p1.center.y := cy;
          p1.radius   := sqrt (sqr (cx-x1) + sqr (cy-y1));
        end;
      if (p1.radius < MAXRADIUS) and (p1.ccur = arcseg) then
        begin
          {Compute rotation direction}
          rotcw := true;
          if x1 = x2 then
            begin
              if y2 < y1 then rotcw := false;
              if x3 < x1 then rotcw := not rotcw;
            end
            else
            begin
              y := ((y2-y1)*x3+y1*x2-y2*x1)/(x2-x1);
              if x2 < x1 then rotcw := false;
              if y3 > y  then rotcw := not rotcw;
            end;
          if rotcw then p1.dir := CW else p1.dir := CCW;

          {Compute starting and ending tangents}
          p1.sang := comparctang (p1, p1.co);
          p1.eang := comparctang (p1, p2.co);
        end
        else
        begin
          p1.ccur := line;
          p1.sang := complinetang (p1.co, p2.co);
          p1.eang := p1.sang;
        end;
    end;

    Hans Passant.
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Saturday, April 11, 2009 3:57 PM
  • I needed something to do also.  This uses gaussian elimination of a N X N matrix to fit three points to a circle.  The points are supplied in a point array and the bounding rectangle of the circle is returned.

    Private Function GetCircleRectFromPoints(ByVal Pts() As Point) As RectangleF
        '(X - Cx)^2 + (Y - Cy)^2 = R^2
        '(R^2 - Cx^2 - Cy^2) + 2X*Cx + 2Y*Cy = X^2 +  Y^2
        'Cr + A*X + B*Y = X^2 + Y^2
        'Solve matrix using gaussian elimination.
        Dim N As Integer = Pts.Length - 1
        Dim X(N, N) As Double, Y(N) As Double
        For I As Integer = 0 To N
          X(I, 0) = 1 : X(I, 1) = Pts(I).X : X(I, 2) = Pts(I).Y
          Y(I) = X(I, 1) * X(I, 1) + X(I, 2) * X(I, 2)
        Next
        Dim MatInv As New Elimination
        MatInv.ComputeCoefficents(X, Y)
        Dim A As Single = CSng(Y(1) / 2)
        Dim B As Single = CSng(Y(2) / 2)
        Dim R As Single = CSng(Math.Sqrt(Y(0) + A * A + B * B))
        Return New RectangleF(A - R, B - R, 2 * R, 2 * R)
      End Function
    
      Public Class Elimination
        Sub ComputeCoefficents(ByVal X(,) As Double, ByVal Y() As Double)
          Dim I, J, K, K1, N As Integer
          N = Y.Length
          For K = 0 To N - 1
            K1 = K + 1
            For I = K To N - 1
              If X(I, K) <> 0 Then
                For J = K1 To N - 1
                  X(I, J) /= X(I, K)
                Next J
                Y(I) /= X(I, K)
              End If
            Next I
            For I = K1 To N - 1
              If X(I, K) <> 0 Then
                For J = K1 To N - 1
                  X(I, J) -= X(K, J)
                Next J
                Y(I) -= Y(K)
              End If
            Next I
          Next K
          For I = N - 2 To 0 Step -1
            For J = N - 1 To I + 1 Step -1
              Y(I) -= X(I, J) * Y(J)
            Next J
          Next I
        End Sub
      End Class
    
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Saturday, April 11, 2009 8:15 PM
  • So anyway if you're for some reason not happy with nobugz' or JohnWein's solution, you can find the math for calculating the coordinates of the circumcenter here:

    http://en.wikipedia.org/wiki/Circumcircle#Cartesian_coordinates

    (Ax, Ay, Bx, By, Cx, Cy are the coordinates of your three points, D is just a temporary variable and the formulas give you x and y coordinates of the center of the circle you're looking for.) I would recommend translating point A to the origin of the coordinate system just like the article suggests. This simplifies the formula a lot. You'll also need the radius of the circle but this is easy to find, you only need to calculate the distance of the center to any of the three points. Keep in mind than if D = 0, the points are collinear and therefore there is no circle going through all of them. (Unless you consider straight line a special case of circle with infinite radius.) Here's an implementation I came up with:
        Private Sub DrawCircle(ByVal a As PointF, ByVal b As PointF, ByVal c As PointF, ByVal g As Graphics)
            g.TranslateTransform(a.X, a.Y)
            b.X -= a.X
            b.Y -= a.Y
            c.X -= a.X
            c.Y -= a.Y
            a.X = 0
            a.Y = 0
            Dim d As Single = 2 * (b.X * c.Y - b.Y * c.X)
            If d = 0 Then
                ' Points a, b, c are collinear
                g.DrawLine(Pens.Black, a.X, a.Y, b.X, b.Y)
                g.DrawLine(Pens.Black, b.X, b.Y, c.X, c.Y)
                g.DrawLine(Pens.Black, c.X, c.Y, a.X, a.Y)
            Else
                Dim center As New PointF((c.Y * (b.X * b.X + b.Y * b.Y) - b.Y * (c.X * c.X + c.Y * c.Y)) / d, (b.X * (c.X * c.X + c.Y * c.Y) - c.X * (b.X * b.X + b.Y * b.Y)) / d)
                Dim radius As Single = CSng(Math.Sqrt(center.X * center.X + center.Y * center.Y))
                g.FillEllipse(Brushes.Blue, center.X - 2, center.Y - 2, 4, 4)
                g.DrawEllipse(Pens.Black, center.X - radius, center.Y - radius, 2 * radius, 2 * radius)
            End If
            g.FillEllipse(Brushes.Red, a.X - 2, a.Y - 2, 4, 4)
            g.FillEllipse(Brushes.Red, b.X - 2, b.Y - 2, 4, 4)
            g.FillEllipse(Brushes.Red, c.X - 2, c.Y - 2, 4, 4)
            g.ResetTransform()
        End Sub
    • Marked as answer by Zhi-Xin Ye Thursday, April 16, 2009 9:31 AM
    Monday, April 13, 2009 1:04 PM
  • Oh yes and actually, there is always only one circle that fits three points, I don't know what gave nobugz the idea that there could be multiple. (Or perhaps you have some picture that would explain this? I hope I'm not forgetting some special case...)
    Monday, April 13, 2009 1:07 PM
  • Yes, my mistake.  There are two when you've got two points and a radius.
    Hans Passant.
    Monday, April 13, 2009 1:10 PM