cos and sin
and are what you use when you want to draw a circle, or animate something moving in a circle.
In the post on unit vectors, we defined the unit -sphere and found a parametrization of it. In the previous post, we encountered the arclength parametrization, which for most purposes is more convenient than other parametrizations. In this post, we discuss the arclength parametrization of the unit circle which is given by the and functions. This will be useful for drawing things along a circle or animating something that's rotating.
Radians
We need to start by naming the length of the unit circle.
By scaling, it follows that the circumference of a circle of radius is given by For example, the circumference of a circle of radius is Equivalently, we could define as the ratio of any circle's circumference to its radius.
cos and sin
Here's a graph to experiment with that.
This completely defines and but doesn't tell us how to compute them. However, in just a few posts we'll derive an algorithm to compute these.
You may be used to a definition of and in terms of ratios of right-angle triangles; I'll review that interpretation below. That interpretation is often useful, but it doesn't work for angles larger than
Animation
Let's say more about animation. So far, and tell us how to parametrize the unit circle, i.e. the circle of radius centered at the origin What about other circles?
Let's do this in steps. First, let
be the circle of radius centered at the origin. We have
so can parametrize this by
Next, let's consider circles centered at other points. For a point and a radius let
be the circle of radius centered at Note that lives in the vector space while lives in the affine space recall that "the origin" is a feature of vector space. We can get by translating to the point in other words,
Therefore, a parametrization of is given by
where
That does everything we need if we want to draw a circle (or a part of a circle). Often though, we want to animate something moving in a circle. This means we need to further take into account its speed (or angular velocity) and its starting position.
By default, starts at and goes around the circle one every units of time, going counterclockwise. To have it start at a different angle we just add that on to
To change the speed, we multiply by a constant. For example, if is measured in seconds and we want to go around the circle once every two seconds, we'd use
since going around the circle once every two seconds is the same as going halfway around the circle every second. We can also scale by a negative number to go clockwise instead of counter-clockwise.
To summarize:
Here's some code to try that out:
import { useEffect, useRef } from "react"; /** Point in 2-dimensional (affine) space */ type Pt2 = [number, number]; type Circle = { /** Measured in [0, 100] x [0, 100] and mapped onto the actual screen later */ center: Pt2; radius: number; }; const SECONDS = 1000, MINUTES = 60 * SECONDS; const CIRCLE = 2 * Math.PI; // scene setup export default function Graph() { const items: MovingProps[] = [ { children: "👻", circle: { center: [50, 50], radius: 25, }, rpm: 45, }, { children: "😈", circle: { center: [25, 25], radius: 20, }, // negative to go clockwise rpm: -30, }, { children: "😍", circle: { center: [75, 40], radius: 10, }, rpm: 15, }, { children: "😡", circle: { center: [30, 60], radius: 20, }, rpm: -60, }, { children: "😡", circle: { center: [70, 60], radius: 20, }, // so it will collide with the other one initialAngle: CIRCLE / 2, rpm: 60, }, ]; return ( <main> {items.map((props) => ( <Moving key={JSON.stringify(props)} {...props} /> ))} </main> ); } // moving divs interface MovingProps { children: React.ReactNode; circle: Circle; /** * Initial angle. * @default 0 */ initialAngle?: number; /** Revolutions per minute. */ rpm: number; } function Moving({ children, circle, initialAngle = 0, rpm }: MovingProp) { const ref = useRef<HTMLDivElement>(null); useEffect(() => { const start = performance.now(); let cancelId: number; // update handler const update = (t: number) => { if (!ref.current) return; const angle = initialAngle + ((CIRCLE * (t - start)) / MINUTES) * rpm; const [x, y] = toScreenCoords(getCoords(circle, angle)); // we want to position the center rather than the top-left corner const rect = ref.current.getBoundingClientRect(); ref.current.style.transform = `translate( ${x - rect.width / 2}px, ${y - rect.height / 2}px )`; cancelId = requestAnimationFrame(update); }; // start animation loop cancelId = requestAnimationFrame(update); // cancel animation loop on unmount return () => cancelAnimationFrame(cancelId); }, []); return ( <div className="w-min text-3xl absolute" ref={ref}> {children} </div> ); } /** Get the coordinates of a point on a circle. */ function getCoords(circle: Circle, angle: number): Pt2 { const { center: [cx, cy], radius: r, } = circle; // we subtract in the second coordinate because screen y-axis // goes down while math y-axis goes up return [cx + r * Math.cos(angle), cy - r * Math.sin(angle)]; } /** Map [0, 100] x [0, 100] onto the actual screen dimensions */ function toScreenCoords([x, y]: Pt2) { return [(window.innerWidth * x) / 100, (window.innerHeight * y) / 100]; }
Triangle interpretation
You may be more used to a definition of and in terms of ratios of angles of right-angle triangles.
This perspective is often useful, but it doesn't work for angles larger than
How does this relate to coordinates of points on the unit circle? First, note that for any point on a circle, we can form a right-angle triangle whose hypotenuse is the line segment from the center of the circle to that point (see below). In the case of the unit circle,
Special angles
Although we don't yet have a general algorithm for computing and we can read off the values of the coordinates of East North West and South
With a bit more work, we can calculate the coordinates of the point at using the triangle interpretation above.
Since the angles of a triangle add up to the remaining angle is also This implies that the two side lengths and are equal; let's call that By the Pythagorean theorem,
Therefore, we have