ソフトウェア/tchart.rb のバックアップソース(No.7)

更新

[[公開メモ]]

#contents

* テキストファイルのソースを与えてタイミング図を作成するスクリプトです [#yf72bb15]

"Timing chart formatter  by kumagai" ~
http://www.mech.tohoku-gakuin.ac.jp/rde/contents/library/tchart/indexframe.html

を参考にして、svg を出力するように ruby で作りました。

svg の使い方はこちらのサイトを参考にさせていただきました。~
http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo.htm

かなり走り書き的なところがありますが、ご容赦下さい。

* 使用例 [#ya5f8236]

次のようなテキストから、

 # AXI4 fundamental protocol
 clock	_~_~_~_~_~_~_~_~_~_~_
 data	=?====X=DATA========X=?====
 valid	_____~~~~~~~~~~______
 ready	_____________[~~]______

このようなタイミング図を SVG として出力します。

&tchart(
# AXI4 fundamental protocol
clock	_~_~_~_~_~_~_~_~_~_~_
data	=?====X=DATA========X=?====
valid	_____~~~~~~~~~~______
ready	_____________[~~]______
);

* こちらのアドレスで試すことができます [#ba570c1c]

オンラインで変換結果を得られます(svn形式 or png形式):~
http://output.jsbin.com/xakesiquga

こちらは javascript で書かれており、サーバーに負荷は掛かりませんので、
そのままタイミングチャートの作成用として、ご利用いただけます。

* タイミング記述テキストの文法 [#g7420e8f]

** # から始まる行はコメント行です [#ld750e11]

 # This line will not appear in the chart
 clk    _~_~_~_~_~
 signal ___~~~~___

&tchart(
# This line will not appear in the chart
clk    _~_~_~_~_~
signal ___~~~~___
);

** @ から始まる行で設定を変更できます [#q06c69eb]

形式は次の通りです。

 @[設定値の名称][スペース][設定値]

[[設定可能な値としてどんな物があるかについてはこちらです。>#rd2a2e82]]

 @signal_style stroke-linecap="round" stroke-width="2" stroke="green" fill="none"
 clk    _~_~_~_~_~
 signal ___~~~~___

&tchart(
@signal_style stroke-linecap="round" stroke-width="2" stroke="green" fill="none"
clk    _~_~_~_~_~
signal ___~~~~___
);

** % から始まる行で自由な位置に文字列を追加できます [#z209ee2f]

与えるパラメータは、次の通りです。

 %[x座標][スペース][y座標][スペース][文字列]

 @margin 20
 %100 -7 test!
 clk    _~_~_~_~_~
 signal ___~~~~___

&tchart(
@margin 20
%100 -7 test!
clk    _~_~_~_~_~
signal ___~~~~___
);

** 空行があれば1行分だけ空白が空きます [#le587f61]

 clk1   _~_~_~_~_~
 clk2   __~~__~~__
 
 signal ___~~_____

&tchart(
clk1   _~_~_~_~_~
clk2   __~~__~~__

signal ___~~_____
);

** その他の文字から始まる行が信号になります [#e6c10712]

フォーマットは

 信号名 [空白] タイミング定義

の形です。

* タイミング定義の文法と用例 [#qdaa4e35]

** _ と ~ で 0 と 1 を表します。 [#dd9965b7]

** - がハイインピーダンス状態です。 [#x82e48ed]

 clk    _~_~_~_~_~_~_~_~_~
 data   ___~~~~__~~____~~_
 enable ___~~~~~~~~~~_____
 output ---~~~~__~~__-----

&tchart(
clk    _~_~_~_~_~_~_~_~_~
data   ___~~~~__~~____~~_
enable ___~~~~~~~~~~_____
output ---~~~~__~~__-----
);

** バス信号や不定値を表すのに = を使えます。 [#r60d0319]

** X で値の切り替えを表せます。 [#uad781ca]

X は時間が進みます。イメージ的には X= のように働きます。

 clk  _~_~_~_~_~
 data =X=X=X=X=X

&tchart(
clk  _~_~_~_~_~
data =X=X=X=X=X
);

** 信号定義に文字列を書き入れることができます。 [#f601e0b1]

** ? を一文字だけ書くと不定値を表すために色が付きます。 [#dc4c5057]

文字列を書いても時間は進みません。

 clk    _~_~_~_~_~_~_~_~_~
 enable ___~~~~~~~~~~_____
 output ---=D0=X=D1X=D2X=D3X=D4-----

&tchart(
clk    _~_~_~_~_~_~_~_~_~
enable ___~~~~~~~~~~_____
output ---=D0=X=D1X=D2X=D3X=D4-----
);

 clk    _~_~_~_~_~_~
 data   =?==XDATA=====X=?=
 valid  ___~~~~~~___

&tchart(
clk    _~_~_~_~_~_~
data   =?==XDATA=====X=?=
valid  ___~~~~~~___
);


** : を入れると空白を入れ、途切れさせることができます。 [#yb67ee5a]

 clk    _~_~_~_:...:~_~_~_~_~
 data   ___~~~~:...:~~____~~_

&tchart(
clk    _~_~_~_:...:~_~_~_~_~
data   ___~~~~:...:~~____~~_
);

** 不定値部分を表すのに、/ \ * が使えます。 [#b5b09f01]

 clk        _~_~_~_~_~_~_~
 rising     ___==/=/=~~~~~
 falling    ~~~==\=\=_____
 transition ___=D0=*=D1*=D2*=D3___

&tchart(
clk        _~_~_~_~_~_~_~
rising     ___==/=/=~~~~~
falling    ~~~==\=\=_____
transition ___=D0=*=D1*=D2*=D3___
);

** | を入れるとグリッド線を引けます。 [#d1263d2a]

** [ ] でくくるとハイライトできます。 [#d41e1c81]

 clk    _~_~_~_~_~_~_~_~_~
 data   ___~~~~~~~~|____~~_
 enable ___[~~~~~~~~~~]_____
 output ---~~~~~~~~__-----

&tchart(
clk    _~_~_~_~_~_~_~_~_~
data   ___~~~~~~~~|____~~_
enable ___[~~~~~~~~~~]_____
output ---~~~~~~~~__-----
);


** 全ての組み合わせを試してみます [#f93875da]

 # :-~_=/\X*
 0 _~_~_~_~_~_~_[~_~_]~_
 
 1 :::-:~:_:=:/:\:X:*:
 2 -:---~-_-=-/-\-X-*-
 3 ~:~-~~~_~=~/~\~X~*~
 4 _:_-_~___=_/_\_X_*_
 5 =:=-=|~=_===/=\=X=*=
 6 /:/-/~/_/=///\/X/*/
 7 \:\-\~\_\=\/\\\X\*\
 8 X:X-X~X_X=X/X\XXX*X
 9 *:*-*~*_*=*/*\*X***

&tchart(
# :-~_=/\X*
0 _~_~_~_~_~_~_[~_~_]~_

1 :::-:~:_:=:/:\:X:*:
2 -:---~-_-=-/-\-X-*-
3 ~:~-~~~_~=~/~\~X~*~
4 _:_-_~___=_/_\_X_*_
5 =:=-=|~=_===/=\=X=*=
6 /:/-/~/_/=///\/X/*/
7 \:\-\~\_\=\/\\\X\*\
8 X:X-X~X_X=X/X\XXX*X
9 *:*-*~*_*=*/*\*X***
);

** 不定値の塗り分けをテスト。 [#h695f4bc]

 @h_line 30
 @h_skip 20
 @w_transient 5
 @w_caption 60
 test =/=?\=\=?/=X=?X=*?=*=X=?-=?= =?X

&tchart(
@h_line 30
@h_skip 20
@w_transient 5
@w_caption 60
test =/=?\=\=?/=X=?X=*?=*=X=?-=?= =?X
);

** 遷移の傾きをなくすには @w_transient 0 とします [#l5475a84]

 @w_transient 0
 clk _~_~_~_~_~_~_~
 data ___|~~~~|_______

&tchart(
@w_transient 0
clk _~_~_~_~_~_~_~
data ___|~~~~|_______
);

** svg の特殊文字も正しくエスケープされます [#b0b8f714]

 clk _~_~_~_~_~_~_~
 data ---|===<D1>=X=<D2>==|---

&tchart(
clk _~_~_~_~_~_~_~
data ---|===<D1>=X=<D2>==|---
);

** 生成された svg には変換元のソースコードが埋め込まれます。 [#c058d952]

下記の CDATA の部分です。ソースコードに CDATA の終了を示す ]]> が現れる場合には、
]]&gt; にエンコードされます。

 LANG:xml
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
      width="240pt" height="60pt" viewBox="-10 -10 220 40" version="1.1">
 <![CDATA[
 clk _~_~_~_~_~_~_~
 data ---|===<D1>=X=<D2>==|---
 ]]>
 <g>
 <text x="35" y="8.5" text-anchor="end" font-size="10" fill="black" font-family="Helvetica">clk</text>
 <path stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none" d="M42,10H52L54,0H64L66,10H76L78,0H88L90,10H100L102,0H112L114,10H124L126,0H136L138,10H148L150,0H160L162,10H172L174,0H184L186,10H196L198,0H208" />
 <text x="35" y="28.5" text-anchor="end" font-size="10" fill="black" font-family="Helvetica">data</text>
 <text x="101.0" y="28.5" text-anchor="middle" font-size="10" fill="black" font-family="Helvetica">&lt;D1&gt;</text>
 <text x="149.0" y="28.5" text-anchor="middle" font-size="10" fill="black" font-family="Helvetica">&lt;D2&gt;</text><path stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none" d="M42,25H52H54H64H66H76L78,20H88H90H100H102H112H114H124L126,30H136H138H148H150H160H162H172L174,25H184H186H196H198H208M76,25L78,30H88H90H100H102H112H114H124L126,20H136H138H148H150H160H162H172L174,25" />
 <path d="M77,-10V40" stroke-linecap="round" stroke-width="0.6" stroke="red" fill="none" />
 <path d="M173,-10V40" stroke-linecap="round" stroke-width="0.6" stroke="red" fill="none" />
 </g>
 </svg>

* 設定値 [#rd2a2e82]

** scale (= 1.0) [#k3b33db2]

図のサイズを拡大・縮小します。
複数指定した場合、最後の値だけが意味を持ちます。

 @scale 1.4
 clk     _~_~_~_~_~_~_~_~
 data	==?=X=D0X=D1X=D2X=D3X=?===

&tchart(
@scale 1.4
clk     _~_~_~_~_~_~_~_~
data	==?=X=D0X=D1X=D2X=D3X=?===
);

 @scale 0.7
 clk     _~_~_~_~_~_~_~_~
 data	==?=X=D0X=D1X=D2X=D3X=?===

&tchart(
@scale 0.7
clk     _~_~_~_~_~_~_~_~
data	==?=X=D0X=D1X=D2X=D3X=?===
);

** margin (= 10) [#b7fe0b8a]
チャートの周囲の余白の幅を指定します。

** w_caption (= 40) [#ge8b8cec]
信号名称部分の幅を指定します。

 long_signal_name	_~~~~__~~~~______~~___

&tchart(
long_signal_name	_~~~~__~~~~______~~___
);

 @w_caption 100
 long_signal_name	_~~~~__~~~~______~~___

&tchart(
@w_caption 100
long_signal_name	_~~~~__~~~~______~~___
);

** w_hold (= 10) [#le7dd0fb]
信号の単位時間から遷移時間を引いた部分の幅を指定します。
途中で変更すれば異なるクロックドメインを表したりできます。

 @w_hold 10
 clock	_~_~_~_~_~_~_~_~_~_~_~
 @w_hold 22
 clock	~_~_~_~_~_~
 @w_hold 16
 clock	_~_~_~_~_~_~_~_

&tchart(
@w_hold 10
clock	_~_~_~_~_~_~_~_~_~_~_~
@w_hold 22
clock	~_~_~_~_~_~
@w_hold 16
clock	_~_~_~_~_~_~_~_
);

** w_transient (= 2) [#n3c4fb61]
信号の遷移時間の幅を指定します。
ゼロを指定すると、遷移のエッジは垂直になります。

 @w_transient 0
 clock	_~_~_~_~_~_~_~_~_~_~_~
 @w_transient 2
 clock	_~_~_~_~_~_~_~_~_~_~_~
 @w_transient 4
 clock	_~_~_~_~_~_~_~_~_~_~_~

&tchart(
@w_transient 0
clock	_~_~_~_~_~_~_~_~_~_~_~
@w_transient 2
clock	_~_~_~_~_~_~_~_~_~_~_~
@w_transient 4
clock	_~_~_~_~_~_~_~_~_~_~_~
);

** h_line (= 10) [#p7e6f34c]
1行の高さを指定します。信号名のフォントサイズもこの値に等しくなります。

 @h_line 10
 clock	_~_~_~_~_~_~_~_~_~_~_~
 @h_line 16
 clock	_~_~_~_~_~_~_~_~_~_~_~

&tchart(
@h_line 10
clock	_~_~_~_~_~_~_~_~_~_~_~
@h_line 16
clock	_~_~_~_~_~_~_~_~_~_~_~
);

** h_space (= 10) [#teb7c9f7]
行間のスペースを指定します。

 clock	_~_~_~_~_~_~_~_~_~_~_~
 data1	_~~~~__~~~~______~~___
 @h_space 20
 data2	_~~~~__~~~~______~~___
 data3	_~~~~__~~~~______~~___

&tchart(
clock	_~_~_~_~_~_~_~_~_~_~_~
data1	_~~~~__~~~~______~~___
@h_space 20
data2	_~~~~__~~~~______~~___
data3	_~~~~__~~~~______~~___
);

** signal_style [#v5ac38b6]
信号線のスタイルを svg の path の属性値の形で与えます。
規定値は次の通りです。

 stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none"

 data1	_~~~~__~~~~______~~___
 @signal_style stroke-linecap="round" stroke-width="2" stroke="green" fill="none"
 data2	_~~~~__~~~~______~~___

&tchart(
data1	_~~~~__~~~~______~~___
@signal_style stroke-linecap="round" stroke-width="2" stroke="green" fill="none"
data2	_~~~~__~~~~______~~___
);

** grid_style [#ra37b0f5]
グリッド線のスタイルを svg の path の属性値の形で与えます。
規定値は次の通りです。

 stroke-linecap="round" stroke-width="0.6" stroke="red" fill="none"



 @grid_style stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none"
 data1	_~~~~__|~~~~______~~___
 @grid_style stroke-linecap="round" stroke-width="0.6" stroke="#0CC" fill="none"
 data2	_~~~~__~~~~______|~~___

&tchart(
@grid_style stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none"
data1	_~~~~__|~~~~______~~___
@grid_style stroke-linecap="round" stroke-width="0.6" stroke="#0CC" fill="none"
data2	_~~~~__~~~~______|~~___
);

** highlight_style [#d1714e07]
ハイライト部分のスタイルを指定します。

規定値は次の通りです。

 stroke="none" fill="#ff8"


 data1	_~~~~__[~~~~]______~~___
 @highlight_style stroke="green" fill="#8f8" stroke-width="1"
 data2	_~~~~__~~~~______[~~]___

&tchart(
data1	_~~~~__[~~~~]______~~___
@highlight_style stroke="green" fill="#8f8" stroke-width="1"
data2	_~~~~__~~~~______[~~]___
);

** notcare_style [#r0fb27ce]

不定値部分のスタイルを指定します。

 fill="#ccc"

 clk     _~_~_~_~_~_~_~_~_~_~_~
 data1	====?=*========*=?======
 @notcare_style stroke="none" fill="#8f8"
 data1	====?=*========*=?======

&tchart(
clk     _~_~_~_~_~_~_~_~_~_~_~
data1	====?=*========*=?======
@notcare_style stroke="none" fill="#8f8"
data1	====?=*========*=?======
);

** caption_font [#z0c3eaff]

信号名のフォントを指定します。

規定値は次の通りです。

 fill="black" font-family="Helvetica"

 clk     _~_~_~_~_~_~_~_~_~_~_~
 @caption_font fill="red" font-family="Helvetica"
 @signal_style stroke="red" fill="none"
 data	_~~~~______~~____~~~~~

&tchart(
clk     _~_~_~_~_~_~_~_~_~_~_~
@caption_font fill="red" font-family="Helvetica"
@signal_style stroke="red" fill="none"
data	_~~~~______~~____~~~~~
);

** signal_font [#sfe73988]

信号部分で用いるフォントを指定します。

規定値は次の通りです。

 fill="black" font-family="Helvetica"

 clk     _~_~_~_~_~_~_~_~
 @signal_font fill="red" font-family="Helvetica"
 data	==?=X=D0X=D1X=D2X=D3X=?===

&tchart(
clk     _~_~_~_~_~_~_~_~
@signal_font fill="red" font-family="Helvetica"
data	==?=X=D0X=D1X=D2X=D3X=?===
);

** rotate (= 0) [#s041342a]

未実装

* ソースファイル [#u33d6dea]

いやほんと、走り書きですみません。

 LANGUAGE:ruby(linenumber)
 #!/usr/bin/ruby
 
 #### tchart.rb by osamu@big.jp
 #
 # "Timing chart formatter  by kumagai" 
 # http://www.mech.tohoku-gakuin.ac.jp/rde/contents/library/tchart/indexframe.html
 #
 # を参考に、svg を出力するようにしたものです
 #
 # かなり走り書き的なところがありますが、ご容赦下さい。
 #
 
 #####################################################################
 
 # default config
 
 conf = {
   scale:          1.0,
   margin:         10,
   w_caption:      40,
   w_hold:         10,
   w_transient:    2,
   h_line:         10,
   h_space:        10,
   signal_style:   'stroke-linecap="round" stroke-width="0.6" stroke="black" fill="none"',
   grid_style:     'stroke-linecap="round" stroke-width="0.6" stroke="red" fill="none"',
   highlight_style:'stroke="none" fill="#ff8"',
   notcare_style:  'fill="#ccc"',
   rotate:         0,
   caption_font:   'fill="black" font-family="Helvetica"',
   signal_font:    'fill="black" font-family="Helvetica"'
 }
 
 #####################################################################
 
 class Line
   class SubLine
     def initialize(x,y)
       @points = [[x,y]]
     end
     def accepts?(x,y)
       @points.last == [x,y]
     end
     def add(x,y)
       @points << [x,y]
     end
     def path
       result = ''
       last = nil
       @points.each do |p|
         if last
           if last==p
             next
           elsif last[0]==p[0]
             result += 'V%g' % p[1]
           elsif last[1]==p[1]
             result += 'H%g' % p[0]
           else
             result += "L%g,%g" % p
           end
         else
           result = "M%g,%g" % p
         end
         last = p
       end
       result
     end
   end
 
   def initialize(style)
     @lines = []
     @style = style
   end
 
   def draw(x1, y1, x2, y2)
     unless line = @lines.find {|line| line.accepts?(x1, y1) }
       line = SubLine.new(x1, y1)
       @lines << line
     end
     line.add(x2, y2)
   end
 
   def svg
     path = @lines.map{|line| line.path }.join('')
     %Q[<path #{@style} d="#{path}" />]
   end
 
 end
 
 class Timeline
   # : - ~ _ = 
   @@transitions = [
   '          ', # :
   '  - 1 4 14', # -
   '  2 ~ / ~/', # ~
   '  3 ` _ _`', # _
   '  23`~/_= ', # =
   '  23`~/_=/', # /
   '  23`~/_=`', # \
   '  23`~/_X ', # X
   '  23`~/_=X', # *
   ]
 
   @@transition_lines = {
     ' ' => [],
     '~' => [[1,1]],
     '_' => [[0,0]],
     '=' => [[1,1],[0,0]],
     'X' => [[1,0],[0,1]],
     '`' => [[1,0]],
     '/' => [[0,1]],
     '1' => [[1,0.5]],
     '2' => [[0.5,1]],
     '3' => [[0.5,0]],
     '4' => [[0,0.5]],
     '-' => [[0.5,0.5]],
   }
 
   #                :  -     ~   _   =
   @@state_lines = [[],[0.5],[1],[0],[0,1]]
 
   #
   @@codes = ':-~_=/\\X*'
 
   @@grids = []
   def self.grids
     @@grids
   end
 
   @@highlight = []
   def self.highlight
     @@highlight
   end
 
   def initialize(conf, y)
     @line = Line.new(conf[:signal_style])
     @current = 0
     @conf = conf
     @x = conf[:w_caption]
     @y = y
     @crosses = []
     @strings = []
   end
   def y(s)
     @y + (1-s) * @conf[:h_line]
   end
   def y0
     @y + @conf[:h_line]
   end
   def y1
     @y
   end
   def yz
     @y + @conf[:h_line]/2.0
   end
   def x
     @x
   end
   def xh
     @x + @conf[:w_transient]/2.0
   end
   def xt
     @x + @conf[:w_transient]
   end
   def xr
     @x + @conf[:w_transient] + @conf[:w_hold]
   end
   def draw_transition_sub(c)
     crosses = ''
     @@transition_lines[c].each do |line|
       @line.draw(x,  y(line[0]), xt, y(line[1]))
       crosses += c if line[0] != line[1]
     end
     crosses
   end
   def draw_transition(s)
     crosses = ''
     @@transitions[s][2*@current,2].each_char do |c|
       crosses += draw_transition_sub(c)
     end
     crosses
   end
   def draw_state(s)
     @@state_lines[s].each do |line|
       @line.draw(xt, y(line), xr, y(line))
     end
   end
   def add(c)
     s = @@codes.index(c)
     crosses = draw_transition(s)
     s = 4 if s > 4
     draw_state(s)
     @crosses << [x, crosses] if crosses!=''
     if (@current == 0 and s != 0) or (@current != 0 and s == 0)
       @crosses << [x, '|']
     end
     @current = s
     @x = xr
   end
   def add_string(s)
     @strings << [@crosses.count, s]
   end
   def parse(line)
     while line != ''
       if line.sub!(/^\s+/, '')
       elsif line.sub!(/^\|/, '')
         @@grids << xh
       elsif line.sub!(/^\[/, '')
         if @@highlight.last==nil or @@highlight.last.is_a?(Array)
           @@highlight << xh
         end
       elsif line.sub!(/^\]/, '')
         if @@highlight.last.is_a?(Numeric)
           @@highlight[-1] = [@@highlight.last, xh]
         end
       elsif line.sub!(/^([:\-~_=\/\\X*])/, '')
         add($1)
       elsif line.sub!(/"(([^"]|"")+)"/, '')
         add_string($1.strip)
       elsif line.sub!(/([^:\-~_=\/\\X*]+)/, '')
         add_string($1.strip)
       end
     end
   end
   def string2svg
     @crosses << [x,'|']
     @strings.map do |string|
       x1 = @crosses[string[0]-1][0]
       x1t = x1 + @conf[:w_transient]
       x1h = x1 + @conf[:w_transient]/2.0
       x1r = x1t + @conf[:w_hold]
       x2 = @crosses[string[0]  ][0]
       x2t = x2 + @conf[:w_transient]
       x2h = x2 + @conf[:w_transient]/2.0
       x2r = x2t + @conf[:w_hold]
 
       # 
       sanitized = string[1].gsub(/</, '&lt;').gsub(/>/, '&gt;').gsub(/"/, '&quot;')
       svg = %Q(<text x="#{(x1h + x2h)/2.0}" y="#{y0-1.5}" text-anchor="middle" font-size="#{@conf[:h_line]}" #{@conf[:signal_font]}>#{sanitized}</text>)
 
       if string[1] == '?'
         line = "M#{x1t},#{y1}H#{x2}"
         case @crosses[string[0]][1]
         when '|'
           ;
         when 'XX'
           line += "L#{x2h},#{yz}"
         when '/'
           line += "H#{x2t}"
         when '`'
           line += "L#{x2t},#{y0}"
         when '23'
           line += "H#{x2t}L#{x2},#{yz}L#{x2t},#{y0}"
         when '14'
           line += "L#{x2t},#{yz}"
         when '1'
           line += "L#{x2t},#{yz}V#{y0}"
         when '2'
           line += "H#{x2t}L#{x2},#{yz}"
         when '3'
           line += "V#{yz}L#{x2t},#{y0}"
         when '4'
           line += "H#{x2t}V#{yz}"
         end
         line += "L#{x2},#{y0}H#{x1t}"
         case @crosses[string[0]-1][1]
         when '|'
           ;
         when 'XX'
           line += "L#{x1h},#{yz}"
         when '/'
           line += "H#{x1}"
         when '`'
           line += "L#{x1},#{y1}"
         when '23'
           line += "L#{x1},#{yz}"
         when '14'
           line += "H#{x1}L#{x1t},#{yz}L#{x1},#{y1}"
         when '1'
           line += "V#{yz}L#{x1},#{y1}"
         when '2'
           line += "H#{x1}V#{yz}"
         when '3'
           line += "L#{x1},#{yz}V{y1}"
         when '4'
           line += "H#{x1}L#{xt},#{yz}"
         end
         line += "Z"
         svg = %Q(\n<path stroke="none" d="#{line}" #{@conf[:notcare_style]}/>) + svg
       end
       svg
     end.join("\n")
   end
   def svg
     string2svg + @line.svg
   end
 end
 
 def caption2svg(conf, y, caption)
   sanitized = caption.gsub(/</, '&lt;').gsub(/>/, '&gt;').gsub(/"/, '&quot;')
   %Q(<text x="#{conf[:w_caption]-5}" y="#{y+conf[:h_line]-1.5}" text-anchor="end" font-size="#{conf[:h_line]}" #{conf[:caption_font]}>#{sanitized}</text>)
 end
 
 #####################################################################
 
 svg = []
 y = -1
 x_max = 0
 lines = readlines
 lines.shift while lines[0] =~ /^\s*$/
 lines.pop while lines.last =~ /^\s*$/
 
 lines.each do |line|
   next if line =~ /^\#/
 
   if line =~ /^@/   #### configuration
     if line !~ /^@([^\s]+)[\s]+([^\s].*)$/
       STDERR.puts "Illegal Line: #{line}"
       next
     end
     if conf[$1.to_sym].is_a?(Numeric)
       conf[$1.to_sym] = $2.to_f
     else
       conf[$1.to_sym] = $2
     end
     next
   end
 
   if line =~ /^%/   #### free string
     if line !~ /^%([\d\.-]+)[\s]+([\d\.-]+)[\s]+([^\s].*)$/
       STDERR.puts "Illegal Line: #{line}"
       next
     end
     svg << %Q(<text x="#{$1}" y="#{$2}" text-anchor="middle" font-size="#{conf[:h_line]}" #{conf[:signal_font]}>#{$3}</text>)
     next
   end
 
   if y < 0
     y = 0
   else
     y += conf[:h_space]
   end
 
   line.sub!(/\s*$/, '')
   next if line == ''
 
   if line !~ /^([^\s]+)[\s]+([^\s].*)$/
     STDERR.puts "Illegal Line: #{line}"
     next
   end
 
   caption_s = $1
   timeline_s = $2
 
   svg << caption2svg(conf, y, caption_s)
   timeline = Timeline.new(conf, y)
   timeline.parse(timeline_s)
   x_max = timeline.xr if x_max < timeline.xr
   svg << timeline.svg
 
   y += conf[:h_line]
 end
 
 m = conf[:margin]
 w = "%g" % ((x_max + 2*m) * conf[:scale])
 h = "%g" % ((y     + 2*m) * conf[:scale])
 l = "%g" % (-m)
 t = "%g" % (-m)
 r = "%g" % (x_max+2*m)
 b = "%g" % (y    +2*m)
 
 Timeline.grids.each do |g|
   x = "%g" % g
   svg << %Q(<path d="M#{x},#{'%g' % (-m/2)}V#{'%g' % (y+m/2)}" #{conf[:grid_style]} />)
 end
 
 Timeline.highlight.each do |h|
   if h.is_a?(Array)
     x1 = "%g" % h[0]
     x2 = "%g" % h[1]
     svg.unshift %Q(<path d="M#{x1},#{'%g' % (-m/2)}V#{'%g' % (y+m/2)}H#{x2}V#{'%g' % (-m/2)}Z" #{conf[:highlight_style]} />)
   end
 end
 
 print <<SVG
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
      width="#{w}pt" height="#{h}pt" viewBox="#{l} #{t} #{r} #{b}" version="1.1">
 <![CDATA[
 #{lines.join("\n").gsub(/\]\]\>/, ']]&gt;')}
 ]]>
 <g>
 #{svg.join("\n")}
 </g>
 </svg>
 SVG

* ちょっと疑問 [#sfbfc96d]

ハイインピーダンスからの遷移の書き方はこれで良いんだろうか?

他と傾きが違ってしまうけれど・・・

 @h_line 30
 @h_skip 20
 @w_transient 8
 @w_caption 100
 test1 ___|~~~|___|==|X=|X=
 test2 ---~~~---==--==

&tchart(
@h_line 30
@h_skip 20
@w_transient 8
@w_caption 100
test1 ___|~~~|___|==|X=|X=
test2 ---~~~---==--==
);

* もしかして、javascript で作った方が良かったんじゃないだろうか・・・ [#m8dba977]

javascript で実装すればクライアント側の処理だけで図を描画できますし、
最近はそのまま画像として保存することもできるそうで、

http://www.pori2.net/html5/Canvas/150.html

ずっと良かったのかもしれません???

確かに・・・ずっとよさそうです~
http://output.jsbin.com/nirugenabu/1

* コメント・質問 [#jb4548c8]

#article_kcaptcha

Counter: 2984 (from 2010/06/03), today: 1, yesterday: 0