How can the following function be implemented in various languages?
Calculate the (x,y)
point on the circumference of a circle, given input values of:
- Radius
- Angle
- Origin (optional parameter, if supported by the language)
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 );
/**
* 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 + "°";
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.
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}");
}
}