和田研フォントキットの肉付け処理を解き明かす (4)
Kazuki Ohta, 2005/05/22
改善点をざっと見た所、rm-geta(in gothic.l)の処理が一番手を付け易そうだったのでこの部分を解析してみる事にした。
さてさて、ソースコードだ。
(defun rm-geta (prim getalen)
(let* ((points (car prim))
(elements (cadr prim))
(newelements)
(linkpoints)
(yokopoints))
(do ((l elements (cdr l))(p)(link))
((atom l))
(and (setq link (assq 'link (cddar l)))
(setq linkpoints (append (cdr link) linkpoints)))
(and (eq (caar l) 'yoko)
(setq p (cadar l))
(setq yokopoints `(,(car p) ,(cadr p) .,yokopoints)))
(or (memq (caar l) '(tate magaritate))
(setq linkpoints (append (cadar l) linkpoints))))
最初のandではlink点をlinkpointsに追加している。次のandでは(yoko (x y))という形式であれば、(x y)をyokopointsに追加している。最後のorは、(tate hogehoge)もしくは(magaritate hogehoge)の場合は何もせずそれ以外の場合(例えばyokoとか)は構成点をlinkpointsにつなげる。
すなわち、結果的にlink点とtate線を構成する要素点以外の点がすべてlinkpointsに追加される。どうでも良いが、このorの使いかたはちょっと勉強になった。if not文の代わりとして使えるのか。
BUG? : (tate (link x1 y1) (link x2 y2))とかなっている場合はlinkpointsに追加されない気がするが、果たして?
(((200.0 15.0) (200.0 44.93975 (LINK-OK T)) (287.22797 56.549038) (260.3886 89.54386) (210.54404 118.26157) (101.26943 57.77107) (154.94818 112.15142) (15.0 124.37173) (383.08292 124.37173) (45.673576 45.85627) (353.36786 45.85627) (303.22415 163.76692) (303.22415 261.85522) (299.65317 249.30905) (298.7604 203.97173) (98.78454 163.76692) (98.78454 260.71466) (98.78454 249.30905) (98.78454 203.97173) (118.71968 277.12338) (118.71968 371.1257) (296.23987 371.1257) (296.23987 332.34976) (338.12665 279.47345) (384.99997 342.14163) (177.56065 264.58972) (243.38274 322.94952) (69.85176 282.6068) (15.0 338.61658)) ((TATE (0 1)) (HIDARI (2 3 4)) (TEN (5 6)) (YOKO (7 8)) (YOKO (9 10) (LINK 1)) (TATE (11 12) (LINK 14 13) (LINK 3 2)) (TATE (15 16) (LINK 18 17) (LINK 7 6)) (YOKO (17 13)) (YOKO (18 14)) (YOKO (15 11)) (KOKORO (19 20 21 22)) (TEN (23 24)) (TEN (25 26)) (TEN (27 28))) (YUNIT . 61.10153) (XUNIT . 129.6496) (XLIMIT 0 400) (CENTER . 200.0))
-- linkpoints -- (27 28 25 26 23 24 19 20 21 22 15 11 18 14 17 13 18 17 14 13 9 10 1 7 8 5 6 2 3 4) -- yokopoints -- (15 11 18 14 17 13 9 10 7 8)
(do ((l elements (cdr l))(epoints)(p1)(lastp)(rp1)(link)(yokolink))
((atom l)
`(,points ,(nreverse newelements) .,(cddr prim)))
(cond ((memq (caar l) '(tate magaritate))
(setq epoints (copy-list (cadar l)))
(setq lastp (last epoints))
(setq rp1 (nth (setq p1 (car lastp)) points))
(setq link (assq 'link (cddar l)))
ほげほげ...
(t (push (car l) newelements))))))
さて、cond文に入ろう。elementがtate or magaritateの場合は最初の長い処理が行われ、そうでない場合は単に今の状態のelementをnewelementsにpushしているだけのようだ。つまりyokoなんかは全く見ていないという事か(意味的には下駄は縦方向にしか現れないという事だ)。epoints. lastp, rp1は(tate 0 1)の場合次のようになる。epointsは線を構成する点のリスト、laspは終端点、rp1は終端点の座標を示しているようだ。linkは現在処理しているelementのlink点のリストである。
-- epoints -- (0 1) -- lastp -- (1) -- rp1 -- (200.0 44.93975 (LINK-OK T))
(and link
(setq yokolink
(do ((ll (cdr link)(cdr ll))(ret))
((atom ll)(nreverse ret))
(and (memeq (car ll) yokopoints)
(push (car ll) ret)))))
(cond ((or (null link)(null yokolink)(memeq p1 linkpoints))
(push (car l) newelements))
(t
(do ((ll yokolink (cdr ll))(minlink)(minlen)(p)(len))
((atom ll)
(cond ((< minlen getalen)
(rplaca lastp minlink)
; (break)
(push `(,(caar l) ,epoints
(link .,(remove minlink (cdr link)))
.,(cddar l)) newelements))
(t
(push (car l) newelements))))
(setq p (nth (car ll) points))
(setq len (metric2 rp1 p))
(and (or (null minlink)(< len minlen))
(setq minlink (car ll) minlen len))))))
その次のが本丸だ。先ほど構築したyokolinkに対して順々に処理を行っている事が分かる。minlenという未知変数が出てきているのでひとまず cond文を飛ばして読み進めよう。pは現在処理しているyokolinkの要素の点の座標。rp1は現在しょりしているelementの終端点の座標。 lenはその二つの距離を取っている。つぎのand文と合わせて考えてみると「終端点と一番近いlink点(それもyokoとlinkしている奴)」がminlink、「終端点とminlinkとの距離」がmlnlenだ。さて、condに戻ろう。そのminlenがgetalenよりも短い場合にはelementの終端点をminlinkにし(rplaca)、さらにlink点からminlinkを取り除く。こうする事で点が一つ取り除かれた、つまり下駄が取り除かれたのだ。図を参考にして頂きたい。
ここまで読めば狩野さんの指摘されている点も理解できたので、後は実際に狩野さんが提示されている方法を実現する手段を考えるのみだな。「縦画を伸ばして、はみ出した部分を元の縦画と重ね合わせるようなアフィン変換」が理解不能。とりあえずアフィン変換をもうちょっと勉強した方が良さそうだな。現在朝の5時。今夜はアフィン変換様に眠気を誘って頂く事にするか。
[ return ]