らんだむな記憶

blogというものを体験してみようか!的なー

rubyとBézierと(2)

簡単な実験が済んだので本番。Bézier曲線を用いて円を近似したい。こういうのはggればすぐに見つかる。
ベジェ曲線で描く円と真円の差についてが詳しいかな?誤差の解析も行ってるみたい。
実はろくに読まずにハンドルの計算だけ拝借して

#! /usr/bin/ruby -Ku

K = 4.0 * (Math.sqrt(2) - 1) / 3

class Point
    attr_reader :x, :y

    def initialize(x, y)
        @x = x; @y = y
    end
end

class Bezier
    def initialize(p0, p1, p2, p3)
        @p0 = p0; @p1 = p1; @p2 = p2; @p3 = p3
    end

    def pos(t)
        x = (1-t)**3 * @p0.x + 3 * (1-t)**2 * t * @p1.x + 3 * (1-t) * t**2 * @p2.x + t**3 * @p3.x
        y = (1-t)**3 * @p0.y + 3 * (1-t)**2 * t * @p1.y + 3 * (1-t) * t**2 * @p2.y + t**3 * @p3.y

        return x, y
    end
end


################################################################################

b = Bezier.new(Point.new(1, 0), Point.new(1, K), Point.new(K, 1), Point.new(0, 1))

0.upto(20) do |i|
    x, y = b.pos(0.05 * i)
    r = Math.sqrt(x**2 + y**2)
    print "(#{x}, #{y}), r=#{r}\n"
end

を実行したところ、

$ ruby bezier_test.rb
(1.0, 0.0), r=1.0
(0.9966850288425443, 0.08201554800834368), r=1.0000537969699292
(0.9869116882454315, 0.16220519420888285), r=1.0001525910698772
(0.9709373375215417, 0.24031157928873675), r=1.0002344567818422
(0.9490193359837564, 0.31607734393502485), r=1.0002712569198766
(0.9214150429449554, 0.38924512883486606), r=1.0002586923825882
(0.8883818177180199, 0.45955757467538005), r=1.0002076876796593
(0.8501770196158307, 0.5267573221436858), r=1.0001371111576869
(0.8070580079512686, 0.590587011926903), r=1.0000678211276561
(0.759282142037214, 0.6507892847121505), r=1.00001803199381
(0.7071067811865476, 0.7071067811865476), r=1.0
(0.6507892847121504, 0.759282142037214), r=1.0000180319938097
(0.5905870119269027, 0.8070580079512686), r=1.000067821127656
(0.5267573221436858, 0.8501770196158308), r=1.0001371111576869
(0.4595575746753799, 0.88838181771802), r=1.0002076876796593
(0.38924512883486606, 0.9214150429449554), r=1.0002586923825882
(0.3160773439350247, 0.9490193359837563), r=1.0002712569198764
(0.24031157928873664, 0.9709373375215418), r=1.0002344567818422
(0.16220519420888282, 0.9869116882454314), r=1.0001525910698772
(0.08201554800834357, 0.9966850288425444), r=1.0000537969699292
(0.0, 1.0), r=1.0

のように妙に円に近いのでびっくりしてリンク先をもっと読んでみたら案外近いんだなぁと。
解析結果にあるように「ベジェ曲線の近似円弧の方が、真円よりも若干大きい」というのが見える。丸め誤差云々があるとしても、結構露骨に見えているので、誤差では覆らない結果だろう。

数式上は半径の計算において、Bézierの3次の項がキャンセルできないことから定数にはなり得ず、決して定数にはならないから円にはならず、結構誤差もでるんだろうな、と思っていたが、意外に綺麗に近似(何をもって綺麗に近似かは定めない)できるんだなぁと思った。

―――――・・・

OpenTypeフォントのグリフとかも3次Bézierなわけだけど、半濁音の○の部分は特別なフォントでもない限り新円に見えるわけで、その表現のために結構アンカーポイントを置いているのかなと思ってたけど、この計算結果なら、4個のアンカーポイントとそれぞれの2本ずつの等長のハンドルだけで十分に綺麗な円が描けるな。
ただ、$4\frac{\sqrt{2} - 1}{3} = 0.552284749...$ なので、半濁点の半径をかけこんで適当な整数近似することになると思うので、理想の最適近似よりはもうちょっと歪むのだろうけどね。

コンピュータで扱うなら、場合によったら、$\sqrt{2}$などは連分数で近似して分数として扱ったほうが途中まで整数計算できて便利かもしれない。いまひとつの連分数展開 - 小人さんの妄想とかに具体的な計算例がある。

連分数は普通に代数学の授業を受けているだけではあまり習わない気もするけど(群, 環, 体, Galois理論, 数論, ... と進むとイマイチ出番が...)、上野先生の本に載ってた気がする。分冊の頃に読んだきりなので忘れたが、この単行本になるのだろうか?→代数入門 (現代数学への入門) | 上野 健爾 |本 | 通販 | Amazon

しかし、代数学はもう少しちゃんとやっとけば良かったね(ちゃんとやったものなど何もないが!)。Banach環や作用素環論に近づくと、途端にいきなり位相の入ったイデアルとか出てきてビックリしちゃうからね。Calkin algebra - Wikipediaとか手の震えが止まらなくなっちゃうからね!