A Ford circle is a circle derived from a pair of numbers that are co-prime, i.e. they have no common factors. For a pair of co-prime integers p and q the Ford circle has radius r and centre at a point

P(x, y) where r = 1/(2q^2) and P = (p/q, r)

No matter what co-prime numbers, p and q, are used to create Ford circles the circles never intersect and they are all tangential to the horizontal axis.

Now, we could generate ‘random’ Ford circles by picking any old co-prime pair (p, q). However, the Farey series is a ‘ready made’ set of such integers – all the Farey fractions are in reduced form and so the numerator and denominator are co-prime. For example, using the code from the previous post,

1 2 3 4 |
-- λ-> farey 10 [0/1,1/10,1/9,1/8,1/7,1/6,1/5,2/9,1/4,2/7,3/10,1/3,3/8,2/5,3/7,4/9,1/2, 5/9,4/7,3/5,5/8,2/3,7/10,5/7,3/4,7/9,4/5,5/6,6/7,7/8,8/9,9/10,1/1] |

To create Ford circles from these fractions we first define a suitable type and then create a few helper functions i.e.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
-- module FordCircles where import Farey import Fractions type FordCircle = (Float, Float, Float) -- for Farey of order n fordCircles :: Integer -> [FordCircle] fordCircles = fmap fordCircle . farey fordCircle :: Fraction -> FordCircle fordCircle (F p q) = (r, fromIntegral p / fromIntegral q, r ) where r = 1/fromIntegral (2*q*q) scaleFordCircle :: Float -> FordCircle -> FordCircle scaleFordCircle s (x, y, z) = (s * x, s * y, s * z) |

Here we’re using the Farey sequence code from module *Farey* and mapping *fordCircle* over a Farey sequence. The function *fordCircle* is just the Haskell form of the equations described above. However, generating a list of *FordCircle* gives data that is not particularly informative. e.g.

1 2 3 |
-- λ-> fordCircles 10 [(0.5,0.0,0.5),(5.0e-3,0.1,5.0e-3),(6.1728396e-3,0.11111111,6.1728396e-3),(7.8125e-3,0.125,7.8125e-3),(1.0204081e-2,0.14285715,1.0204081e-2),(1.3888889e-2,0.16666667,1.3888889e-2),(2.0e-2,0.2,2.0e-2),(6.1728396e-3,0.22222222,6.1728396e-3),(3.125e-2,0.25,3.125e-2),(1.0204081e-2,0.2857143,1.0204081e-2),(5.0e-3,0.3,5.0e-3),(5.5555556e-2,0.33333334,5.5555556e-2),(7.8125e-3,0.375,7.8125e-3),(2.0e-2,0.4,2.0e-2),(1.0204081e-2,0.42857143,1.0204081e-2),(6.1728396e-3,0.44444445,6.1728396e-3),(0.125,0.5,0.125),(6.1728396e-3,0.5555556,6.1728396e-3),(1.0204081e-2,0.5714286,1.0204081e-2),(2.0e-2,0.6,2.0e-2),(7.8125e-3,0.625,7.8125e-3),(5.5555556e-2,0.6666667,5.5555556e-2),(5.0e-3,0.7,5.0e-3),(1.0204081e-2,0.71428573,1.0204081e-2),(3.125e-2,0.75,3.125e-2),(6.1728396e-3,0.7777778,6.1728396e-3),(2.0e-2,0.8,2.0e-2),(1.3888889e-2,0.8333333,1.3888889e-2),(1.0204081e-2,0.85714287,1.0204081e-2),(7.8125e-3,0.875,7.8125e-3),(6.1728396e-3,0.8888889,6.1728396e-3),(5.0e-3,0.9,5.0e-3),(0.5,1.0,0.5)] |

But, using a little bit of Gloss…

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
-- import Farey import FordCircles import Graphics.Gloss circFull :: FordCircle -> Picture circFull (r, x, y) = translate x y $ color white $ circleSolid r circFrame :: FordCircle -> Picture circFrame (r, x, y) = translate x y $ circle r makeCircles :: Picture makeCircles = Pictures $ fmap (circFull . scaleFordCircle 500) (fordCircles 50) makeCircles' :: Picture makeCircles' = Pictures $ fmap (circFrame . scaleFordCircle 500) (fordCircles 50) main :: IO () main = display FullScreen black $ Pictures [makeCircles, translate 500 0 $ rotate 180 makeCircles] |

we can generate an attractive interpretation of the data…

Notice how the circles don’t intersect, are tangential to immediate neighbours and to the x axis.

and with a bit of manipulation…

With high contrast…

Quite pleasing images for little effort 🙂

All the code is on Github and thanks for reading!