和田研フォントキットの肉付け処理を解き明かす -(3)-

和田研フォントキットの肉付け処理を解き明かす - (2) -を踏まえて。


さて本日は"cross"を解き明かす事にする。"cross"とは四点の集まりを現しており、"kazari"を定義する際に必要となるモノ。まずは肉づけ処理を行うskeleton2list関数内でcross2point関数がどのように呼ばれるかを確認してみる。

       ; 1本の線しか通らない場合
       ((and (eq 2 (length link))(<= 0 (cadadr link) 1))
        (setq part1 (cadr link))
        (setq type1 (cadr part1))
        (setq cross (cross2point part1 (nth (caar ll) points)))

この時cross2point関数に渡される値は、点番号9の場合次の様になる。点番号9は漢字”六”の最終画の終端点である(右はらいの終端点)。

--------- part1 -----------
(MIGI 1
 (LINES
  (((ANGLE 219.82303 172.279) (BEZIER 244.4074 259.43265)
    (BEZIER 276.72842 319.24637) (ANGLE 343.47696 392.01614))
   ((ANGLE 241.84372 166.0674) (BEZIER 264.38162 245.96619)
    (BEZIER 303.05368 305.12848) (ANGLE 364.8063 372.45166)))))

--------- points -----------
((17.008724 94.82636) (383.90768 94.82636) (201.42883 93.091 (LINK-OK T))
 (201.42883 15.0) (135.34949 172.92671) (98.54841 289.2855) (15.000006 385.0)
 (230.83337 169.1732) (263.65594 285.53195) (353.17212 383.1232))

--------- (nth (caar ll) points) -----------
(353.17212 383.1232)

つまりcross2point関数には、ある一点を通るアウトラインのデータとその点の座標が渡されている事が分かる。さて、次に実際にcross2point関数を見て行く事にしよう。

;                                                                                                                                              
; partからpointへの垂線とその他の2点                                                                                        
;                                                                                                                                              
(defun cross2point (part1 point)
  (let ((ret (make-array 4))
        (line0 (caadr (assq 'lines (cddr part1))))
        (line1 (cadadr (assq 'lines (cddr part1)))))
    (case (cadr part1)
             (0
              (setq line0 (list (car line0)(cadr line0)))
              (setq line1 (list (car line1)(cadr line1))))
             (1
              (setq line0 (reverse line0) line1 (reverse line1))
              (setq line0 (list (car line0)(cadr line0)))
              (setq line1 (list (car line1)(cadr line1)))))

まずはretという4要素の配列を作成している。この事からcrossは四つの値から成り立つ事が分かる。line0, line1はアウトラインである。次の処理は始点(flag == 0)の時と終点(flag == 1)の時に分かれている。始点の時はline0・line1共に最初の2要素だけを取り出し、終点の場合は最後の2要素を取り出してリストにしている。例えば点番号9の場合、この処理を終えた後のline0, line1の値はline0 == ( (ANGLE 219.82303 172.279) (BEZIER 244.4074 259.43265)), line1 == ( (ANGLE 241.84372 166.0674) (BEZIER 264.38162 245.96619))となっている。

さて、次々行こう。

    (let* ((p0 (nearest line0 point))
           (p1 (nearest line1 point))
           (l00 (list (float (cadar line0))(float (caddar line0))))
           (l01 (list (float (cadadr line0))(float (cadr (cdadr line0)))))
           (l10 (list (float (cadar line1))(float (caddar line1))))
           (l11 (list (float (cadadr line1))(float (cadr (cdadr line1))))))

nearest関数ではline0におけるpointからの垂線の足を求める。コードは省く(段々適当になってきた(笑))

l00, l01, l10, l11はそれぞれline0. line1から数字の値だけを取り出したものである。点番号9の場合、l00 == (219.82303 172.279), l01 == (244.4074 259.43265), l10 == (241.84372 166.0674), l11 == (264.38162 245.96619)となる。

とりあえず、現在の関係を図に現してみよう。図は点番号3、つまり漢字"六”の一画目の始点の場合である。コードと見比べてみると良く分かると思う。この場合はたまたまp0とl00, p1とl10が一致している。

さて、ここまで来れば後は簡単だ。

          (cond
           ((or (null p0)(null p1))
            (setq p0 (list (float (car point))(float (cadr point))))
            (vset ret 0 p0)
            (vset ret 1 p0)
            (vset ret 2 p0)
            (vset ret 3 p0))
           (t
            (vset ret 0 p0)
            (vset ret 1 p1)
            (vset ret 2
                  (plus2
                   p0
                   (normlen2 (metric2 p0 p1)
                             (diff2 l01 l00))))
            (vset ret 3
                  (plus2
                   p1
                   (normlen2 (metric2 p0 p1)
                             (diff2 l11 l10))))))
      ret)))

前半はエラー処理だと思われる(違うかな?)。とにかくp0, p1が見付からなかった場合に呼ばれる。肝心なのはその後だ。最初に生成したret配列に次々に値を代入していく。コードを追いかけると、次のような四点が求まっている事が分かる。この正方形こそがcrossの正体だ。

(続く)