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