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