21 Dec, 2020     21 Dec, 2020

C#: Calculate positions on the circumference of a circle

Programming, .NET, C#

Imagine a regular clock-face. 12 o’clock is at the top-most position and 11 more numbers around the outside edge at regular angles from the centre.

Recently I needed to find the location of points on the circumference of a circle. Using an angle and the distance from the origin of the circle (radius), I needed the coordinates (x and y). This should have been a simple exercise and it (mostly) was but it is not one I want to repeat again. For alas, the Pythagorean Theorem and I are somewhat estranged. To say that I am rusty is an understatement.

As such, here is how I found points on the circumference of a circle, given the angle and radius.

Let’s get started, full source code at the bottom.

 

0. The Explanation
This is essentially some straight-forward maths.

  • We are give a rectangle. Find the largest regular circle that fits inside and place it in the centre of the rectangle.
  • Given an angle and the distance from the origin, calculate the x and y position of a point that intersects the circumference.
  • Take the position of the point and offset it with the origin. Offset each quadrant accordingly.
  • Let’s use degrees for the angle rather than radians

 

1. The Exceptions
Before we get trigger-happy with the Pythagorean Theorem, there are four exceptions that can be simplified.

The 0°, 90°, 180° and 270° all have one element in the coordinate that is zero and the other is the radius.


public PointF GetPoint(int deg, int distance)
{
    PointF origin = this.Origin;
    PointF rs = new PointF()
    {
        X = origin.X,
        Y = origin.Y
    };

    if (deg == 0)
    {
        rs.Y -= distance;
    }
    else if (deg == 90)
    {
        rs.X += distance;
    }
    else if (deg == 180)
    {
        rs.Y += distance;
    }
    else if (deg == 270)
    {
        rs.X -= distance;
    }
    else
    {
        /// todo: top-right
        /// todo: bottom-right
        /// todo: bottom-left
        /// todo: top-left
    }

    return rs;
}

 

2. The Trig’
Now we apply the maths, the trigonometry is the same but treat each quadrant separately. This way we are simply changing the way we offset the origin with the point position.


    else if ((deg > 0) && (deg < 90))
    {
        rs.X += (float)(distance * Math.Sin(calcToRad(deg)));
        rs.Y -= (float)(distance * Math.Cos(calcToRad(deg)));
    }
    else if ((deg > 90) && (deg < 180))
    {
        rs.X += (float)(distance * Math.Cos(calcToRad(deg - 90)));
        rs.Y += (float)(distance * Math.Sin(calcToRad(deg - 90)));
    }
    else if ((deg > 180) && (deg < 270))
    {
        rs.X -= (float)(distance * Math.Sin(calcToRad(deg - 180)));
        rs.Y += (float)(distance * Math.Cos(calcToRad(deg - 180)));
    }
    else if ((deg > 270) && (deg < 360))
    {
        rs.X -= (float)(distance * Math.Cos(calcToRad(deg - 270)));
        rs.Y -= (float)(distance * Math.Sin(calcToRad(deg - 270)));
    }

 

A. Code Usage
I created a class in a similar way to the System.Drawing.Rectangle.

This is an example using the class to paint a 12-point clock-face


protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    Graphics g = e.Graphics;

    Circle circle = new Circle(this.DisplayRectangle);
    int radius = 60;

    for (int i=0; i<12; i++)
    {
        PointF pos = circle.GetPoint((i * 30), radius);

        g.DrawString((i == 0 ? 12 : i).ToString(), this.Font, new SolidBrush(Color.Black), pos);
    }

}

 

Well, that’s that.

I hope someone finds this interesting or useful.
 

Code Repository