Shades of Gray

Last year I drew some things, and today I recreated one of them using Haskell with the Diagrams library. It features a circular Gray code with five bits, giving 32 possible combinations. A Gray code has the property that neighbouring combinations change only in one position, which is useful for detecting errors in rotary encoders.
Here is the source code:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Diagrams.Prelude hiding (size)
import Diagrams.Backend.SVG.CmdLine (B, defaultMain)
main :: IO ()
main = defaultMain diagram
diagram :: Diagram B
diagram
= bg white
. frame 0.125
. centerXY
. mconcat
$ [ ring 6 2 1
, ring 5 4 2
, ring 4 8 4
, ring 3 16 8
, ring 2 16 0
, txt
]
ring :: Double -> Int -> Int -> Diagram B
ring inr count offset
= mconcat
. zipWith (cell inr) [0..31]
. drop offset
. cycle
$ ( replicate count False
++ replicate count True
)
cell :: Double -> Double -> Bool -> Diagram B
cell inr angle flag
= rotate (angle / 32 @@ turn)
. fc (if flag then dark else light)
. lc white
. translate (r2 (i, 0))
. strokeT
. closeTrail
$ mconcat
[ p2 (i, 0) ~~ p2 (o, 0)
, arc' o h t
, rotate t $ p2 (o, 0) ~~ p2 (i, 0)
, arc' i (rotate t h) t'
]
where
t = 1 / 32 @@ turn
t' = -1 / 32 @@ turn
i = size inr
o = size (inr + 1)
h = direction (r2 (1, 0))
light, dark :: Colour Double
light = sRGB 0.7 0.7 0.7
dark = sRGB 0.3 0.3 0.3
size :: Double -> Double
size r = exp ((r - 7) / 5)
txt :: Diagram B
txt
= scale 0.125
. centerXY
. vcat
$ centerXY
. atop (translateY 0.4 $ strutY 0.8)
. font "LMSans10"
. italic
. fontSizeL 1
. text
<$> words "32 Shades of Gray"
I had some frustrations until I realized that fontSizeL was what I needed to make the text change size when rendering at different sizes.