Tufte の話でよく紹介される、ナポレオンのロシア遠征みたいなのを描いてみたらどうなるだろうかと思って、そのプリミティブとして太さが変わる線の書き方を考えてみる。
太さが変わる線を、平面上の二つの(半径が異なる)円をつなぐと考える。cairo では (というか API の元ネタは PostScript だと思うが) 中心・半径・角度を指定して円弧をかけるので、問題は角度である。
頭の中でしばらく考えたがわからなかったので仕方なく実際に図を描いて解いた。

#!/usr/bin/ruby1.8
require 'cairo'
format = Cairo::FORMAT_ARGB32
width = 300
height = 200
surface = Cairo::ImageSurface.new(format, width, height)
context = Cairo::Context.new(surface)
context.set_source_rgb(1, 1, 1)
context.rectangle(0, 0, width, height)
context.fill
x1, y1 = width * 0.2, height * 0.7
r1 = height * 0.2
x2, y2 = width * 0.9, height * 0.2
r2 = height * 0.05
la = Math.hypot(x2-x1, y2-y1)
dr = r1-r2
lb = Math.sqrt(la*la - dr*dr)
tu = Math.atan2(y2-y1, x2-x1)
tv = Math.atan2(lb, dr)
ta = tu + tv
tb = tu - tv
context.set_source_rgb(0.8, 0.8, 0.8)
context.move_to(x1, y1)
context.arc(x1, y1, r1, ta, tb)
context.fill
context.move_to(x2, y2)
context.arc(x2, y2, r2, tb, ta)
context.fill
context.set_source_rgb(0.4, 0.4, 0.4)
context.set_line_width(1)
context.arc(x1, y1, r1, 0, Math::PI*2)
context.stroke
context.arc(x2, y2, r2, 0, Math::PI*2)
context.stroke
context.set_source_rgb(1, 0, 0)
context.set_line_width(4)
context.arc(x1, y1, r1, ta, tb)
context.arc(x2, y2, r2, tb, ta)
context.close_path
context.stroke
surface.write_to_png("circle2.png")
system("feh circle2.png")[latest]