update.
[chise/ruby.git] / chise / kage.rb
1 # Copyright (C) 2002-2004 Kouichirou Eto, All rights reserved.
2
3 require "sgl"
4 require "singleton"
5 require "chise/kageserver"
6
7 #こんな感じのフォーマットになっている。
8 #<?xml version="1.0"?>
9 #<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
10 #<svg viewBox="0 0 1000 1000">
11 #<path d="M 50,540 950,255 " style="fill: none; stroke: black; stroke-width: 10; stroke-linecap: round;"/>
12 #<path d="M 330,50 330,900 M 330,900 Q 330,950 380,950 M 380,950 840,950 M 840,950 Q 890,950 915,850 " style="fill: none; stroke: black; stroke-width: 10; stroke-linecap: round;"/>
13 #</svg>
14
15 module StrokeFont
16   class QuadraticPath # 動的な分割に対応できるようにする
17     DEFAULT_DIVIDE = 4
18
19     def initialize(p1, p2, p3)
20       @p1, @p2, @p3 = p1, p2, p3
21       @num = DEFAULT_DIVIDE
22     end
23
24     def divide_adaptic #適応的分割数をする。
25     end
26
27     def divide()
28       divide_num(@num)
29     end
30
31     def divide_num(num) #分割数を指定できる
32       #p [num]
33       x1, y1 = @p1
34       x2, y2 = @p2
35       x3, y3 = @p3
36       #2次のbezier曲線の計算式、 P(t) = (1-t)^2*P1 + 2*t*(1-t)*P2 + t^2*P3
37       curve = []
38       (num+1).times {|i|    #ここで最後の点を含めないのがポイント。これによって次の曲線との重複が無いようにしている。
39         t = (i.to_f)/num
40         x = (1-t)*(1-t)*x1 + 2*t*(1-t)*x2 + t*t*x3
41         y = (1-t)*(1-t)*y1 + 2*t*(1-t)*y2 + t*t*y3
42         curve << [x,y]
43       }
44       #p curve
45       curve
46     end
47
48   end
49   
50   class PathResolver
51     #M 50,540 950,255 
52     #M 330,50 330,900 M 330,900 Q 330,950 380,950 M 380,950 840,950 M 840,950 Q 890,950 915,850 
53
54     def initialize
55       reset
56     end
57
58     def reset
59       @lines = []
60       @px, @py = -1, -1
61     end
62
63     def parse(str)
64       reset
65       cmd = []
66       str.split.each {|par|
67         if par.length == 1 #コマンドである
68           exec_cmd(cmd) if 0 < cmd.length #前のコマンドが残っていたら実行
69           cmd = [par] #cmdを新規生成
70         elsif par =~ /[,0-9]+/ #座標値である
71           sx, sy = par.split(/,/)
72           x, y = sx.to_i, sy.to_i
73           cmd << [x, y] #cmdへのargを追加
74         end
75       }
76       exec_cmd(cmd) if 0 < cmd.length #前のコマンドが残っていたら実行
77       @lines
78     end
79
80     def exec_cmd(cmd)
81       c = cmd.shift #先頭をとる
82       case c
83       when "M"
84         cmd.each {|x, y| moveto(x, y) }
85       when "Q"
86         quadratic([@px, @py], cmd.shift, cmd.shift)
87       end
88     end
89
90     def moveto(x, y)
91       @lines << [@px, @py, x, y] if @px != -1
92       @px, @py = x, y
93     end
94
95     def quadratic(p1, p2, p3)
96       #p ["quadratic", p1, p2, p3]
97       qp = QuadraticPath.new(p1, p2, p3)
98       curve = qp.divide
99       curve.each {|x, y|
100         moveto(x, y)
101       }
102     end
103
104   end
105
106   class KageParser
107     def self.parse(svg)
108       @strokes = Strokes.new
109       pr = PathResolver.new
110       lines = svg.split(/\n/)
111       lines.each {|line|
112         next unless line =~ /^<path/
113         if line =~ /d=\"([a-zA-Z0-9, ]+)\"/
114           #lines = KageParser.parse_path($1)
115           lines = pr.parse($1)
116           lines.each {|px, py, x, y|
117             @strokes.add_line(px, 1000-py, x, 1000-y)
118           }
119         end
120       }
121       return @strokes
122     end
123   end
124
125   class KageGlyph # てなかんじ
126     def initialize(code, svg)
127       @code = code
128       @svg = svg
129       @strokes = nil
130     end
131     attr_reader :strokes
132
133     def parse
134       return if @strokes
135       @strokes = KageParser.parse(@svg)
136     end
137
138     def init
139       parse if @strokes.nil?
140     end
141   end
142
143   class KageFont
144     def initialize
145       @server = KageServer.instance
146       @glyphs = []
147       @cache_list = @server.list_cache
148       @rend = nil
149       @rend = StrokesRenderer.new
150       # @rend.hsv = [13, 100, 100]
151       # @rend.hsv = [0, 0, 0]
152       # @rend.hsv = [13, 100, 100] #黄色
153       @rend.hsv = [6, 100, 100] #オレンジ
154     end
155     attr_reader :cache_list, :server
156
157     def get(code)
158       return @glyphs[code] if @glyphs[code]
159       svg = @server.get(code)
160       return nil if svg.nil?
161       @glyphs[code] = KageGlyph.new(code, svg)
162     end
163
164     def init(code)
165       glyph = get(code)
166       return if glyph.nil?
167       glyph.init
168       glyph.parse
169       @rend.set_strokes(glyph.strokes)
170     end
171
172     def draw(code)
173       glyph = get(code)
174       return if glyph.nil?
175       @rend.draw
176     end
177
178     def print(code)
179       char = Character.new(@code)
180       printf("[%s][%04x]\n", char.nil? ? "nil" : char.map_sjis, code)
181     end
182
183   end
184 end