Matthias Dittgen

November 4, 2022

Link circles

In this blog post, we’ll discuss how to smoothly connect circular curves, hereinafter referred to as arcs. I’ll reveal why I’m interested in this topic in a future blog post, but for now, let’s dive into the mechanics of arcs.

In many of my previous posts, I’ve utilized already Svelte and SVG to represent mathematical and geometric concepts. While SVG offers several types of bezier curves, either quadratic (Q, q) or cubic (C, c).

Q x1 y1 x y
q dx1 dy1 dx dy

C x1 y1 x2 y2 x y
c dx1 dy1 dx2 dy2 dx dy

It’s also possible to draw arcs (A a) with SVG. You’ll see immediately that there are more parameters involved.

A rx ry x-axis-rotation large-arc-flag sweep-flag x y
a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy

However, there are a lot differences between bezier curves and arcs. A smooth connection of bezier curves is done by reflecting the previous control point, so the connected curves have the same slope at the transition point. Because of that, the SVG standard even provides abbreviations for quadratic (S, s) and cubic (T, t) follow-up curves.

T x y
t dx dy

S x2 y2 x y
s dx2 dy2 dx dy

Connection of two arcs»

To get a seamless transition of two arcs the last point of the first arc and at the first point of the second arc have to share the same tangent.


I could take this as a starting point for a much deeper comparison against bezier curves, but this shall not be the topic.

Connection of three arcs»

In fact this post is about a smooth connection of one arc with another arc using an additional arc in between as shown in the figure.


And just like back in the math class at school, certain values are given and we have yet to figure out others. Given are radius and position of circle A and circle B as well as the distance between both circles. Furthermore we know the radius of circle C.

And to draw the dashed line using a SVG path we need to find the four waypoints between the three arcs.

ra rc rc rb ra dist rb AC AB BC

AC = ra + rc
AB = ra + dist + rb
BC = rb + rc
export let ra;
export let rb;
export let rc;
export let dist;

$: ac = ra + rc; $: ab = ra + dist + rb; $: bc = rb + rc;

First we can calculate the distances between the three points. This is simple addition from radius and distance values.

Not only do we need the distances, we also need the center of the third circle. With this information, we can determine the two waypoints where the circular segments merge into each other.

The centers of all three circles form an irregular triangle. And that can be broken down into two right triangles.


Pythagoras»

We have adjusted the captions in the figure below. There we can see that we need to find the length of x, x’ and y to describe the coordinates of C relative to A or B.

And with the right angles, the whole thing screams Pythagoras. Can you hear it? ;)

x y z x' z' x y AC BC AB- x

I:         z2 = x2 + y2
          AC2 = x2 + y2
           y2 = AC2 - x2

II:       z'2 =   x'2   + y2
          BC2 = (AB-x)2 + y2
          BC2 = (AB-x) * (AB-x) + y2
          BC2 = AB2 - 2*AB*x + x2 + y2

I in II:  BC2 = AB2 - 2*AB*x + x2 + AC2 - x2
          BC2 = AB2 - 2*AB*x + AC2
       2*AB*x = AB2 + AC2 - BC2

            x = (AB2 + AC2 - BC2) / 2*AB

 x in I:    y = (AC2 - x2)1/2

We have these two triangle, so we have two Pythagorean equations.

And because we can describe x’ as AB - x both equations share the same two unknowns, x and y.

We can plug the parts into each other and rearrange them by x or y.

In Svelte this is a quite easy calculated with reactive statements ($:). The Svelte compiler takes care of everything and re-calculates whenever the dependencies will change.

	$: x = (ab*ab + ac*ac - bc*bc) / (2*ab);
	$: y = Math.sqrt(ac*ac - x*x);

As soon as I have the center point of circle C, I can find the waypoints using linear interpolation along AC and BC. The first and last waypoint we receive by substraction or addition of the radius.

const wayPoint1 = minus(A, point(ra, 0));
const wayPoint2 = plerp(A, C, ra / (ra + rc));
const wayPoint3 = plerp(B, C, rb / (rb + rc));
const wayPoint4 = plus(B, point(rb, 0));

Demo time»

Putting all parts together you can now play with all the parameters and find different circle connections in this interactive demo. Below you see the SVG path description along the waypoints.

radius a: 100 radius a: 100
radius b: 60 radius b: 60
radius c: 45 radius c: 45
distance: 40 distance: 40
show circles: 1 show circles: 1
show normals: 0 show normals: 0
show values: 0 show values: 0
show path: 1 show path: 1
M 60.00 130.00 A 100.00 100.00 1.00 0.00 1.00 246.21 79.32 A 45.00 45.00 1.00 0.00 0.00 317.14 88.01 A 60.00 60.00 1.00 0.00 1.00 420.00 130.00

Next»

Stay tuned for part two, where we’ll connect even more circles.