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]