Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I calculate a point on a circle’s circumference?

The parametric equation for a circle is

x = cx + r * cos(a)
y = cy + r * sin(a)

Where r is the radius, cx,cy the origin, and a the angle.

That's pretty easy to adapt into any language with basic trig functions. Note that most languages will use radians for the angle in trig functions, so rather than cycling through 0..360 degrees, you're cycling through 0..2PI radians.


Here is my implementation in C#:

    public static PointF PointOnCircle(float radius, float angleInDegrees, PointF origin)
    {
        // Convert from degrees to radians via multiplication by PI/180        
        float x = (float)(radius * Math.Cos(angleInDegrees * Math.PI / 180F)) + origin.X;
        float y = (float)(radius * Math.Sin(angleInDegrees * Math.PI / 180F)) + origin.Y;

        return new PointF(x, y);
    }

Who needs trig when you have complex numbers:

#include <complex.h>
#include <math.h>

#define PI      3.14159265358979323846

typedef complex double Point;

Point point_on_circle ( double radius, double angle_in_degrees, Point centre )
{
    return centre + radius * cexp ( PI * I * ( angle_in_degrees  / 180.0 ) );
}

Implemented in JavaScript (ES6):

/**
    * Calculate x and y in circle's circumference
    * @param {Object} input - The input parameters
    * @param {number} input.radius - The circle's radius
    * @param {number} input.angle - The angle in degrees
    * @param {number} input.cx - The circle's origin x
    * @param {number} input.cy - The circle's origin y
    * @returns {Array[number,number]} The calculated x and y
*/
function pointsOnCircle({ radius, angle, cx, cy }){

    angle = angle * ( Math.PI / 180 ); // Convert from Degrees to Radians
    const x = cx + radius * Math.sin(angle);
    const y = cy + radius * Math.cos(angle);
    return [ x, y ];

}

Usage:

const [ x, y ] = pointsOnCircle({ radius: 100, angle: 180, cx: 150, cy: 150 });
console.log( x, y );

Codepen

/**
 * Calculate x and y in circle's circumference
 * @param {Object} input - The input parameters
 * @param {number} input.radius - The circle's radius
 * @param {number} input.angle - The angle in degrees
 * @param {number} input.cx - The circle's origin x
 * @param {number} input.cy - The circle's origin y
 * @returns {Array[number,number]} The calculated x and y
 */
function pointsOnCircle({ radius, angle, cx, cy }){
  angle = angle * ( Math.PI / 180 ); // Convert from Degrees to Radians
  const x = cx + radius * Math.sin(angle);
  const y = cy + radius * Math.cos(angle);
  return [ x, y ];
}

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

function draw( x, y ){

  ctx.clearRect( 0, 0, canvas.width, canvas.height );
  ctx.beginPath();
  ctx.strokeStyle = "orange";
  ctx.arc( 100, 100, 80, 0, 2 * Math.PI);
  ctx.lineWidth = 3;
  ctx.stroke();
  ctx.closePath();

  ctx.beginPath();
  ctx.fillStyle = "indigo";
  ctx.arc( x, y, 6, 0, 2 * Math.PI);
  ctx.fill();
  ctx.closePath();
  
}

let angle = 0;  // In degrees
setInterval(function(){

  const [ x, y ] = pointsOnCircle({ radius: 80, angle: angle++, cx: 100, cy: 100 });
  console.log( x, y );
  draw( x, y );
  document.querySelector("#degrees").innerHTML = angle + "&deg;";
  document.querySelector("#points").textContent = x.toFixed() + "," + y.toFixed();

}, 100 );
<p>Degrees: <span id="degrees">0</span></p>
<p>Points on Circle (x,y): <span id="points">0,0</span></p>
<canvas width="200" height="200" style="border: 1px solid"></canvas>

Calculating point around circumference of circle given distance travelled.
For comparison... This may be useful in Game AI when moving around a solid object in a direct path.

enter image description here

public static Point DestinationCoordinatesArc(Int32 startingPointX, Int32 startingPointY,
    Int32 circleOriginX, Int32 circleOriginY, float distanceToMove,
    ClockDirection clockDirection, float radius)
{
    // Note: distanceToMove and radius parameters are float type to avoid integer division
    // which will discard remainder

    var theta = (distanceToMove / radius) * (clockDirection == ClockDirection.Clockwise ? 1 : -1);
    var destinationX = circleOriginX + (startingPointX - circleOriginX) * Math.Cos(theta) - (startingPointY - circleOriginY) * Math.Sin(theta);
    var destinationY = circleOriginY + (startingPointX - circleOriginX) * Math.Sin(theta) + (startingPointY - circleOriginY) * Math.Cos(theta);

    // Round to avoid integer conversion truncation
    return new Point((Int32)Math.Round(destinationX), (Int32)Math.Round(destinationY));
}

/// <summary>
/// Possible clock directions.
/// </summary>
public enum ClockDirection
{
    [Description("Time moving forwards.")]
    Clockwise,
    [Description("Time moving moving backwards.")]
    CounterClockwise
}

private void ButtonArcDemo_Click(object sender, EventArgs e)
{
    Brush aBrush = (Brush)Brushes.Black;
    Graphics g = this.CreateGraphics();

    var startingPointX = 125;
    var startingPointY = 75;
    for (var count = 0; count < 62; count++)
    {
        var point = DestinationCoordinatesArc(
            startingPointX: startingPointX, startingPointY: startingPointY,
            circleOriginX: 75, circleOriginY: 75,
            distanceToMove: 5,
            clockDirection: ClockDirection.Clockwise, radius: 50);
        g.FillRectangle(aBrush, point.X, point.Y, 1, 1);

        startingPointX = point.X;
        startingPointY = point.Y;

        // Pause to visually observe/confirm clock direction
        System.Threading.Thread.Sleep(35);

        Debug.WriteLine($"DestinationCoordinatesArc({point.X}, {point.Y}");
    }
}