タイミングチャート清書サービス の履歴(No.9)
更新- 履歴一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- ソフトウェア/タイミングチャート清書サービス へ行く。
設置アドレス†
http://dora.bk.tsukuba.ac.jp/~takeuchi/tchart.html
スクリーンイメージ†
概要†
このようなテキストを入力すると、
aclk ~_~_~_~_~_~_~_~_~_~_ awaddr ==?X=A1==X=A2X=A3==X==?=X=A4X=? awvalid __~~~~~~~~~~____~~__ awready ____[~~~~]__[~~]____[~~]__ wdata ====?X=D1X=D2X=?X=D3X=?X=D4==X=? wvalid ____~~~~__~~__~~~~__ wready ____~~~~__~~____~~__ bresp ====00================ bvalid ____~~~~__~~____~~__ bready ~~~~~~~~~~~~~~~~~~1~~
こんなタイミングチャートを生成できるサービスです。
&tchart(
aclk ~_~_~_~_~_~_~_~_~_~_
awaddr ==?X=A1==X=A2X=A3==X==?=X=A4X=?
awvalid ~~~~~~~~~~~~
awready __[~~~~][~~]__[~~]
wdata ====?X=D1X=D2X=?X=D3X=?X=D4==X=?
wvalid __~~~~~~~~~~
wready __~~~~~~__~~
bresp ====00================
bvalid __~~~~~~__~~
bready ~~~~~~~~~~~~~~~~~~1~
);
ソースコード編集中に、ほぼリアルタイムで清書結果を確認できるので、試行錯誤しているうちに正しく書けると思います。
清書したタイミングチャートは SVG または PNG 形式で保存できます。
入力内容がサーバーに送られることはなく、完全にブラウザ上だけで画像を生成していますので、守秘義務の問題も発生しません。心配であればリンク先の html を PC に保存して、そこから立ち上げれば、完全にオフラインの状態でも動作します。
設置アドレスはこちらです:
http://dora.bk.tsukuba.ac.jp/~takeuchi/tchart.html
>>>> 記述文法についてはこちらをご覧下さい。
コメントや質問などがあればこのページの一番下に投稿フォームがありますのでご活用下さい。
このサービスで使っている javascript コードを既存システムに組み込むことで、 各種 Wiki や Markdown にタイミングチャート記述機能を追加できると思います。 下記 組み込み処理系 を参照して下さい。
不具合†
IE では下記リンク先で議論されている問題のため、[Save as PNG] および [Generate Images for Copy]
のボタンが正常に働きません。
https://connect.microsoft.com/IE/feedback/details/809823/draw-svg-image-on-canvas-context
代わりに、SVG 画像上で右クリックから [名前を付けて画像を保存] で形式を PNG にすれば保存できます。
同様に右クリックから [コピー] で画像をクリップボードへ入れられるので、 Word などへ貼り付ける用途ではむしろ IE の方が便利かもしれません。
IE 以外でクリップボードへのコピーの仕方†
セキュリティ上の制限のため、javascript からクリップボードへ 自動的にコピーすることはできないようになっています。 そこで、手動で対応していただく必要があります。
[Generate Images for Copy] ボタンを押すと SVG ソースと PNG 画像が生成されます
- SVG ソースをご入り用であれば、SVG ソースにフォーカスが当たっている状況でそのまま Ctrl+C すればコピー可能です
- PNG 画像がご入り用であれば、PNG 画像上で右クリックから [画像をコピー] します
- なぜか空の HTML も一緒にコピーされてしまうようで、なおかつこの空の HTML が優先的に貼り付けられるようなので、Word などへ貼り付ける際には [形式を選択して貼り付け]-[ビットマップ(DIB)] などとして下さい(Office でのショートカットは Alt+E,S です)
- 上記の制限のため IE では [Generate Images for Copy] で PNG 画像が生成されません
- 代わりに SVG 画像上の右クリックから [コピー] でコピーできますので、そちらをご利用下さい
謝辞†
熊谷正朗さんの "Timing chart formatter by kumagai"
http://www.mech.tohoku-gakuin.ac.jp/rde/contents/library/tchart/indexframe.html
を大いに参考にさせていただきました。
記述文法はほぼ熊谷さんの tchart を踏襲しています。
大変有用なツールをご提供くださったことに感謝いたします。
目次†
- 設置アドレス
- 概要
- 謝辞
- 目次
- タイミング記述テキストの文法
- タイミング定義の文法と用例
- _ と ~ で 0 と 1 を表します。
- - がハイインピーダンス状態です。
- バス信号や不定値を表すのに = を使えます。
- X で値の切り替えを表せます。
- 信号定義に文字列を書き入れることができます。
- 文字列に特殊文字を含めたい場合には " " でくくります
- " を含めたければ "" のように重ねます
- 右寄せ、左寄せ
- ? を一文字だけ書くと不定値を表すために色が付きます。
- : を入れると空白を入れ、途切れさせることができます。
- 不定値部分を表すのに、/ \ * が使えます。
- | を入れるとグリッド線を引けます。
- [ ] でくくるとハイライトできます。
- 全ての組み合わせを試してみます
- 不定値の塗り分けをテスト。
- 遷移の傾きをなくすには @w_transient 0 とします
- svg の特殊文字も正しくエスケープされます
- 生成された svg には変換元のソースコードが埋め込まれます。
- 設定値
- ソースコード
- 組み込み処理系
- コメント・質問
タイミング記述テキストの文法†
# から始まる行はコメント行です†
# This line will not appear in the chart clk _~_~_~_~_~ signal ___~~~~___
&tchart(
# This line will not appear in the chart
clk _~_~_~_~_
signal ~~~~
);
@ から始まる行で設定を変更できます†
形式は次の通りです。
@[設定値の名称][スペース][設定値]
@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 ~~~~
);
% から始まる行で自由な位置に文字列を追加できます†
与えるパラメータは、次の通りです。
%[x座標][スペース][y座標][スペース][文字列]
@margin 20 %100 -7 test! clk _~_~_~_~_~ signal ___~~~~___
&tchart(
@margin 20
%100 -7 test!
clk _~_~_~_~_
signal ~~~~
);
空行があれば1行分だけ空白が空きます†
clk1 _~_~_~_~_~ clk2 __~~__~~__ signal ___~~_____
&tchart(
clk1 _~_~_~_~_
clk2 ~~~~__
signal ~~__ );
その他の文字から始まる行が信号定義になります†
フォーマットは
信号名 [空白] タイミング定義
の形です。
タイミング定義の文法と用例†
_ と ~ で 0 と 1 を表します。†
- がハイインピーダンス状態です。†
clk _~_~_~_~_~_~_~_~_~ data ___~~~~__~~____~~_ enable ___~~~~~~~~~~_____ output ---~~~~__~~__-----
&tchart(
clk _~_~_~_~_~_~_~_~_
data _~~~~~~____~~_
enable ~~~~~~~~~~__
output ---~~~~~~-----
);
バス信号や不定値を表すのに = を使えます。†
X で値の切り替えを表せます。†
X は時間が進みます。イメージ的には X= のように働きます。
clk _~_~_~_~_~ data =X=X=X=X=X
&tchart(
clk _~_~_~_~_
data =X=X=X=X=X
);
信号定義に文字列を書き入れることができます。†
文字列を書いても時間は進みません。
clk _~_~_~_~_~_~_~_~_~ enable ___~~~~~~~~~~_____ output ---=D0=X=D1X=D2X=D3X=D4-----
&tchart(
clk _~_~_~_~_~_~_~_~_
enable ~~~~~~~~~~__
output ---=D0=X=D1X=D2X=D3X=D4-----
);
文字列に特殊文字を含めたい場合には " " でくくります†
" を含めたければ "" のように重ねます†
スペースも入れられます
"=" -======"A = B"- " -======" ""A"" = ""B"" "- A -======A- space -======"A "-
&tchart( "=" -======"A = B"- " -======" ""A"" = ""B"" "- A -======A- space -======"A "- );
右寄せ、左寄せ†
文字列は通常センタリングされますが、_<_ あるいは _>_ から始めることで 左寄せ、右寄せにできます
left -======"_<_DATA"- middle -======DATA- right -======"_>_DATA"-
&tchart( left -======"_<_DATA"- middle -======DATA- right -======"_>_DATA"- );
? を一文字だけ書くと不定値を表すために色が付きます。†
clk _~_~_~_~_~_~ data =?==XDATA=====X=?= valid ___~~~~~~___
&tchart(
clk _~_~_~_~_~_
data =?==XDATA=====X=?=
valid ~~~~~~
);
: を入れると空白を入れ、途切れさせることができます。†
clk _~_~_~_:...:~_~_~_~_~ data ___~~~~:...:~~____~~_
&tchart(
clk _~_~_~_:...:~_~_~_~_
data ~~~~:...:~~_~~_
);
不定値部分を表すのに、/ \ * が使えます。†
clk _~_~_~_~_~_~_~ rising ___==/=/=~~~~~ falling ~~~==\=\=_____ transition ___=D0=*=D1*=D2*=D3___
&tchart(
clk _~_~_~_~_~_~_
rising ___==/=/=~~~~
falling ~~~==\=\=_____
transition =D0=*=D1*=D2*=D3
);
| を入れるとグリッド線を引けます。†
[ ] でくくるとハイライトできます。†
clk _~_~_~_~_~_~_~_~_~ data ___~~~~~~~~|____~~_ enable ___[~~~~~~~~~~]_____ output ---~~~~~~~~__-----
&tchart(
clk _~_~_~_~_~_~_~_~_
data ~~~~~~~~|_~~_
enable [~~~~~~~~~~]__
output ---~~~~~~~~__-----
);
全ての組み合わせを試してみます†
# :-~_=/\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***
);
不定値の塗り分けをテスト。†
@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 とします†
@w_transient 0 clk _~_~_~_~_~_~_~ data ___|~~~~|_______
&tchart(
@w_transient 0
clk _~_~_~_~_~_~_
data |~~~~|____
);
svg の特殊文字も正しくエスケープされます†
clk _~_~_~_~_~_~_~ data ---|===<D1>=X=<D2>==|---
&tchart(
clk _~_~_~_~_~_~_
data ---|===<D1>=X=<D2>==|---
);
生成された svg には変換元のソースコードが埋め込まれます。†
下記の CDATA の部分です。ソースコードに CDATA の終了を示す ]]> が現れる場合には、 ]]> にエンコードされます。
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"><D1></text> <text x="149.0" y="28.5" text-anchor="middle" font-size="10" fill="black" font-family="Helvetica"><D2></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>
設定値†
括弧内が何も指定しないときの既定値です。
scale (= 1.0)†
図のサイズを拡大・縮小します。 複数指定した場合、最後の値だけが意味を持ちます。
@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)†
チャートの周囲の余白の幅を指定します。
w_caption (= 40)†
信号名称部分の幅を指定します。
long_signal_name _~~~~__~~~~______~~___
&tchart( long_signal_name _~~~~~~~~_~~ );
@w_caption 100 long_signal_name _~~~~__~~~~______~~___
&tchart( @w_caption 100 long_signal_name _~~~~~~~~_~~ );
w_hold (= 10)†
信号の単位時間から遷移時間を引いた部分の幅を指定します。 途中で変更すれば異なるクロックドメインを表したりできます。
@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)†
信号の遷移時間の幅を指定します。 ゼロを指定すると、遷移のエッジは垂直になります。
@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)†
1行の高さを指定します。信号名のフォントサイズもこの値に等しくなります。
@h_line 10 clock _~_~_~_~_~_~_~_~_~_~_~ @h_line 16 clock _~_~_~_~_~_~_~_~_~_~_~
&tchart(
@h_line 10
clock _~_~_~_~_~_~_~_~_~_~_
@h_line 16
clock _~_~_~_~_~_~_~_~_~_~_
);
h_space (= 10)†
行間のスペースを指定します。
clock _~_~_~_~_~_~_~_~_~_~_~ data1 _~~~~__~~~~______~~___ @h_space 20 data2 _~~~~__~~~~______~~___ data3 _~~~~__~~~~______~~___
&tchart(
clock _~_~_~_~_~_~_~_~_~_~_
data1 _~~~~~~~~_~~
@h_space 20
data2 _~~~~~~~~_~~
data3 _~~~~~~~~_~~
);
signal_style†
信号線のスタイルを 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†
グリッド線のスタイルを 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†
ハイライト部分のスタイルを指定します。
規定値は次の通りです。
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†
不定値部分のスタイルを指定します。
fill="#ccc"
clk _~_~_~_~_~_~_~_~_~_~_~ data1 ====?=*========*=?====== @notcare_style stroke="none" fill="#8f8" data1 ====?=*========*=?======
&tchart(
clk _~_~_~_~_~_~_~_~_~_~_
data1 ====?=*========*=?======
@notcare_style stroke="none" fill="#8f8"
data1 ====?=*========*=?======
);
caption_font†
信号名のフォントを指定します。
規定値は次の通りです。
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†
信号部分で用いるフォントを指定します。
規定値は次の通りです。
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)†
未実装
ソースコード†
こちらでも参照可能です:http://jsbin.com/lezujowaba/edit?html,css,js,output
html:
LANG:html(linenumber) <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> <script src="https://rawgit.com/eligrey/FileSaver.js/master/FileSaver.min.js"></script> <script src="https://rawgit.com/eligrey/canvas-toBlob.js/master/canvas-toBlob.js"></script> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Timing Chart Formatter</title> </head> <body> <h1>Timing Chart Formatter</h1> <textarea id="source" cols="60" rows="10" spellcheck="false"> clock _~_~_~_~_~_~_~_~_~_~_ data =?====X=DATA========X=?==== valid _____~~~~~~~~~~______ ready _____________[~~]______ </textarea> <div id="result"></div> <input type="button" value="Save as SVG" id="as_svg" /> <input type="button" value="Save as PNG" id="as_png" /> <input type="button" value="Generate Images for Copy" id="for_copy" /> Background: <input id="background" value="white" style="width:60px" /> <div id="images"></div> </body> </html>
スタイル定義:
LANG:scss(linenumber) @import "compass"; $line-height: 18px; textarea { font-size: 15px; line-height: $line-height; padding: 0px 10px; } #source { @include background-image( linear-gradient(left, white 5px, transparent 5px), linear-gradient(right, white 5px, transparent 5px), linear-gradient(white $line-height - 1px, #ddd $line-height - 1px, #ddd $line-height, white $line-height) ); background-size: 100% 100%, 100% 100%, 100% $line-height; }
tchart.coffee
LANG:coffeescript(linenumber) class SvgPath constructor: (style)-> @segments = [] @style = style draw: (x1, y1, x2, y2)-> @segments.push([x1,y1,x2,y2]) svg: -> for s in @segments s[4]= 0 s[5]= null for s1 in @segments for s2 in @segments if s1[2]==s2[0] and s1[3]==s2[1] s1[4] += 1 s2[4] += 1 for s1 in @segments for s2 in @segments continue if s2[4]<2 or s1[2]!=s2[0] or s1[3]!=s2[1] s1[5]= s2 s2[4]= -1 break continue if s1[5] for s2 in @segments continue if s2[4]!=1 or s1[2]!=s2[0] or s1[3]!=s2[1] s1[5]= s2 s2[4]= -1 break path = [] for s1 in @segments continue if s1[4]<0 s = s1 cx = s[0] cy = s[1] path.push "M#{cx},#{cy}" while s if cx == s[2] && cy == s[3] else if cx == s[2] if path[path.length-1][0]=='V' path[path.length-1]= "V#{s[3]}" else path.push "V#{s[3]}" else if cy == s[3] if path[path.length-1][0]=='H' path[path.length-1]= "H#{s[2]}" else path.push "H#{s[2]}" else path.push "L#{s[2]},#{s[3]}" cx = s[2] cy = s[3] s = s[5] """<path #{@style} d="#{path.join('')}" />\n""" class TimeLine # : - ~ _ = @transitions = [ ' ', # : ' - 1 4 14', # - ' 2 ~ / ~/', # ~ ' 3 ` _ _`', # _ ' 23`~/_= ', # = ' 23`~/_=/', # / ' 23`~/_=`', # \ ' 23`~/_X ', # X ' 23`~/_=X', # * ] @transitionLines = ' ' : [] '~' : [[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]] # : - ~ _ = @stateLines = [[],[0.5],[1],[0],[0,1]] # @codes = ':-~_=/\\X*' constructor: (config, y)-> @config = config @x = config.w_caption @y = y @path = new SvgPath(config.signal_style) @current = 0 @crosses = [] @strings = [] @grids = [] @highlights = [] ys: (s)-> @y + (1-s) * @config.h_line y0: -> @y + @config.h_line y1: -> @y yz: -> @y + @config.h_line/2.0 xh: -> @x + @config.w_transient/2.0 xt: -> @x + @config.w_transient xr: -> @x + @config.w_transient + @config.w_hold parse: (line)-> while line != '' if maches = /^\s+/.exec(line) ; else if maches = /^\|/.exec(line) @grids.push [@xh(), @config.grid_style] else if maches = /^\[/.exec(line) if @highlights.length==0 or Array.isArray(@highlights[@highlights.length-1]) @highlights.push @xh() else if maches = /^\]/.exec(line) if @highlights.length>0 and not Array.isArray(@highlights[@highlights.length-1]) @highlights[@highlights.length-1] = [@highlights[@highlights.length-1], @xh(), @config.highlight_style] else if matches = /^([:\-~_=\/\\X*])/.exec(line) @addState(matches[1]) else if matches = /^"(([^"]|"")*)"/.exec(line) @addString(matches[1].replace(/""/g, '"').replace(/[ ]/g, ' ')) else if matches = /([^:\-~_=\/\\X*\|\]\[]+)/.exec(line) @addString(matches[1]) line = line.substr(matches[0].length, line.length-matches[0].length) @processStrings() + @path.svg() addState: (c)-> s = TimeLine.codes.indexOf(c) crosses = @drawTransition(s) s = 4 if s > 4 @drawState(s) @crosses.push([@x, crosses]) if crosses!='' if (@current == 0 and s != 0) or (@current != 0 and s == 0) or (@crosses.length==0 and s == 0) @crosses.push([@x, '|']) @current = s @x = @xr() addString: (s)-> @strings.push [@crosses.length, s.replace(/^\s+|\s+$/g, '')] drawTransition: (s)-> crosses = '' transitions = TimeLine.transitions[s].substr(2*@current,2) for i in [0..transitions.length-1] crosses += @drawTransitionSub(transitions[i]) crosses drawTransitionSub: (c)-> crosses = '' for line in TimeLine.transitionLines[c] @path.draw(@x, @ys(line[0]), @xt(), @ys(line[1])) crosses += c if line[0] != line[1] crosses drawState: (s)-> for line in TimeLine.stateLines[s] @path.draw(@xt(), @ys(line), @xr(), @ys(line)) processStrings: -> svg = [] @crosses.push [@x,'|'] for string in @strings y0 = @ys(0) y1 = @ys(1) yz = @ys(0.5) x1 = @crosses[string[0]-1][0] x1t = x1 + @config.w_transient x1h = x1 + @config.w_transient/2.0 x1r = x1t + @config.w_hold x2 = @crosses[string[0] ][0] x2t = x2 + @config.w_transient x2h = x2 + @config.w_transient/2.0 x2r = x2t + @config.w_hold if string[1]=='?' path= ["M#{x1t},#{y1}H#{x2}"] path.push switch @crosses[string[0]][1] when '|' then "" when 'XX' then "L#{x2h},#{yz}" when '/' then "H#{x2t}" when '`' then "L#{x2t},#{y0}" when '23' then "H#{x2t}L#{x2},#{yz}L#{x2t},#{y0}" when '14' then "L#{x2t},#{yz}" when '1' then "L#{x2t},#{yz}V#{y0}" when '2' then "H#{x2t}L#{x2},#{yz}" when '3' then "V#{yz}L#{x2t},#{y0}" when '4' then "H#{x2t}V#{yz}" path.push "L#{x2},#{y0}H#{x1t}" path.push switch @crosses[string[0]-1][1] when '|' then "" when 'XX' then "L#{x1h},#{yz}" when '/' then "H#{x1}" when '`' then "L#{x1},#{y1}" when '23' then "L#{x1},#{yz}" when '14' then "H#{x1}L#{x1t},#{yz}L#{x1},#{y1}" when '1' then "V#{yz}L#{x1},#{y1}" when '2' then "H#{x1}V#{yz}" when '3' then "L#{x1},#{yz}V{y1}" when '4' then "H#{x1}L#{xt},#{yz}" path.push "Z" svg.push """\n<path stroke="none" d="#{path.join('')}" #{@config.notcare_style}/>""" sanitized = string[1].replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') if string[1].substr(0,3)=='_<_' x = x1t anchor = 'start' sanitized = sanitized.substring(6) else if string[1].substr(0,3)=='_>_' x = x2 anchor = 'end' sanitized = sanitized.substring(6) else x = (x1h+x2h)/2.0 anchor = 'middle' svg.push """<text x="#{x}" y="#{@ys(0)-1.5}" text-anchor="#{anchor}" """ + """font-size="#{@config.h_line}" #{@config.signal_font}>#{sanitized}</text>\n""" svg.join("") class TimingChart @config: 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"' constructor: (config= {})-> @config= {} @setConfig(TimingChart.config) @setConfig(config) setConfig: (config)-> @config[k]= v for own k,v of config parse: (source)-> @svg= [] @grids= [] @highlights= [] @y= -1 @x_max= @config.w_caption source= source.replace(/^\n+/, '') source= source.replace(/\n+$/, '') for line in source.split("\n") @parseLine(line) @processGrids() @processHighlights() @formatSVG(source) formatSVG: (source)-> m= @config.margin w= (@x_max + 2 * m) * @config.scale * 1.3 h= (@y + 2 * m) * @config.scale * 1.3 @width = w @height = h @svg = """ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="#{w}px" height="#{h}px" viewBox="#{-m} #{-m} #{@x_max+2*m} #{@y+2*m}" version="1.1"> <![CDATA[ #{source.replace(/\]\]\>/g, ']]>')} ]]> <g> #{@svg.join("\n")} </g> </svg> """ parseLine: (line)-> return if line[0] == '#' #### comment if line[0]=='@' #### configuration if !(matches = /^@([^\s]+)[\s]+([^\s].*)$/.exec(line)) throw new SyntaxError("Illegal Line: #{line}") if $.isNumeric(@config[matches[1]]) @config[matches[1]] = Number(matches[2]) else @config[matches[1]] = matches[2] return if line[0] == '%' #### free string if !(matches = /^%(-?[\d\.]+)\s+(-?[\d\.]+)\s+?(.*)$/.exec(line)) throw new SyntaxError("Illegal Line: #{line}") @svg.push("""<text x="#{matches[1]}" y="#{matches[2]}" text-anchor="middle" """ + """font-size="#{@config.h_line}" #{@config.signal_font}>#{matches[3]}</text>""") return if @y<0 @y= 0 else @y+= @config.h_space line= line.replace(/\s*$/, '') return if line=='' #### empty line if !(matches = /^([^\s]+)[\s]+([^\s].*)$/.exec(line)) throw new SyntaxError("Illegal Line: #{line}") @formatCaption(matches[1]) @formatTimeline(matches[2]) @y+= @config.h_line formatCaption: (caption)-> sanitized = caption.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') @svg.push( """<text x="#{@config.w_caption-5}" y="#{@y+@config.h_line-1.5}" text-anchor="end" """+ """font-size="#{@config.h_line}" #{@config.caption_font}>#{sanitized}</text>""" ) formatTimeline: (line)-> tline= new TimeLine(@config, @y) @svg.push tline.parse(line) @x_max= tline.x if tline.x > @x_max for g in tline.grids @grids.push g for h in tline.highlights @highlights.push h processGrids: -> t = -@config.margin/2.0 b = @y+@config.margin/2.0 for g in @grids @svg.push """<path d="M#{g[0]},#{t}V#{b}" #{g[1]} />""" processHighlights: -> t = -@config.margin/2.0 b = @y+@config.margin/2.0 for h in @highlights if Array.isArray(h) @svg.unshift """<path d="M#{h[0]},#{t}V#{b}H#{h[1]}V#{t}Z" #{h[2]} />""" lastSource = '' lastChart = null update = -> source = $('#source').val() return if lastSource==source lastSource = source lastChart= new TimingChart() svg= lastChart.parse(source) $('#result').html(svg) $('#images>*').remove() callbackWithCanvas = (f)-> canvas = document.createElement('canvas') canvas.width = lastChart.width canvas.height = lastChart.height # http://nmi.jp/archives/223 img = new Image() img.onload = -> ctx = canvas.getContext("2d") ctx.fillStyle = $('#background').val() ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.drawImage(img, 0, 0, canvas.width, canvas.height) f(canvas) img.src = "data:image/svg+xml;base64," + btoa(lastChart.svg) (exports ? this).TimingChart= TimingChart
起動処理
LANG:coffeescript $ -> setInterval(update, 100) $('#as_svg').on 'click', -> update() blob = new Blob([lastChart.svg], {type: "image/svg+xml"}); saveAs(blob, 'timing-chart.svg') $('#as_png').on 'click', -> update() callbackWithCanvas (canvas)-> canvas.toBlob (blob)-> saveAs(blob, "timing-chart.png") $('#for_copy').on 'click', -> $('#images>*').remove() update() $('#images').append $('<h2>').html('SVG Source') textarea = $('<textarea cols="60" rows="3">').val(lastChart.svg) $('#images').append textarea textarea.focus() textarea.select() $('#images').append $('<h2>').html('PNG Image') callbackWithCanvas (canvas)-> $('#images').append $('<img>').attr('src', canvas.toDataURL())
組み込み処理系†
このサービスで作成した svg を手で切り貼りして html に埋め込む代わりに、 html 中に以下のように直接タイミング図定義を書いておいて、 javascript で svg に変換して表示した方が、 あとから修正することなどを考えると便利です。
LANG:html <script type="text/tchart"> aclk ~_~_~_~_~_~_~_~_~_~_ awaddr ==?X=A1==X=A2X=A3==X==?=X=A4X=? awvalid __~~~~~~~~~~____~~__ awready ____[~~~~]__[~~]____[~~]__ wdata ====?X=D1X=D2X=?X=D3X=?X=D4==X=? wvalid ____~~~~__~~__~~~~__ wready ____~~~~__~~____~~__ bresp ====00================ bvalid ____~~~~__~~____~~__ bready ~~~~~~~~~~~~~~~~~~1~~ </script>
上記の起動処理部分を
LANG:coffeescript $ -> $('script[type="text/tchart"]').each -> script = $(this) script.replaceWith (new TimingChart()).parse(script.html())
としたものを <head> の中で読み込めば、そのような自動変換が可能です。 javascript に直したものを下に置いておきます。jquery を取り込んだ後で読み込むようにして下さい。
似たような方法を使えば、Wiki や Blog をカスタマイズして、 タイミング図の表示機能を付けるようなことも簡単にできると思います。 Markdown ならこうでしょうか。
ちなみにこのサイト自身、タイミングチャートを処理するプラグイン を導入した pukiwiki で書いているため、 記事の中で自然にタイミング図を記述できています。 (じゃなければ、このページの文法説明あたりを書くのはかなり気が滅入る作業になります・・・)
実行例†
実行例はこちらをご覧下さい:
http://jsbin.com/weyiyodoze/edit?html,js,output
右上の Auto-run JS をチェックしておけば、 左側の html を編集してリアルタイムに結果を確認することができます。
html 記述:
LANG:html <!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> <script src="tchart.min.js"></script> <script> $(function(){ $('script[type="text/tchart"]').each(function(){ var script = $(this); script.replaceWith( TimingChart.format(script.html()) ); }); }); </script> </head> <body> <p>次の、script で囲まれた部分が SVG の図として表示されます。</p> <script type="text/tchart"> aclk ~_~_~_~_~_~_~_~_~_~_ awaddr ==?X=A1==X=A2X=A3==X==?=X=A4X=? awvalid __~~~~~~~~~~____~~__ awready ____[~~~~]__[~~]____[~~]__ wdata ====?X=D1X=D2X=?X=D3X=?X=D4==X=? wvalid ____~~~~__~~__~~~~__ wready ____~~~~__~~____~~__ bresp ====00================ bvalid ____~~~~__~~____~~__ bready ~~~~~~~~~~~~~~~~~~1~~ </script> </body> </html>
ソースコード†
tchart.min.js :
LANG:javascript // This script defines 'this.TimingChart'. // USAGE: // var chart = new TimingChart(); // var svg = chart.parse(source); // var w = chart.width; // var h = chart.height; // Or, if you don't need width and height: // var svg = TimingChart.format(source); // (function(){var t,s,i,n={}.hasOwnProperty;t=function(){function t(t){this.segments=[],this.style=t}return t.prototype.draw=function(t,s,i,n){return this.segments.push([t,s,i,n])},t.prototype.svg=function(){var t,s,i,n,e,h,r,o,g,c,a,l,f,u,p,y,x,d,_,w,m,v,L,H,S,b,k;for(m=this.segments,r=0,a=m.length;a>r;r++)n=m[r],n[4]=0,n[5]=null;for(v=this.segments,o=0,l=v.length;l>o;o++)for(e=v[o],L=this.segments,g=0,f=L.length;f>g;g++)h=L[g],e[2]===h[0]&&e[3]===h[1]&&(e[4]+=1,h[4]+=1);for(H=this.segments,c=0,u=H.length;u>c;c++){for(e=H[c],S=this.segments,d=0,p=S.length;p>d;d++)if(h=S[d],!(h[4]<2||e[2]!==h[0]||e[3]!==h[1])){e[5]=h,h[4]=-1;break}if(!e[5])for(b=this.segments,_=0,y=b.length;y>_;_++)if(h=b[_],1===h[4]&&e[2]===h[0]&&e[3]===h[1]){e[5]=h,h[4]=-1;break}}for(i=[],k=this.segments,w=0,x=k.length;x>w;w++)if(e=k[w],!(e[4]<0))for(n=e,t=n[0],s=n[1],i.push("M"+t+","+s);n;)t===n[2]&&s===n[3]||(t===n[2]?"V"===i[i.length-1][0]?i[i.length-1]="V"+n[3]:i.push("V"+n[3]):s===n[3]?"H"===i[i.length-1][0]?i[i.length-1]="H"+n[2]:i.push("H"+n[2]):i.push("L"+n[2]+","+n[3])),t=n[2],s=n[3],n=n[5];return"<path "+this.style+' d="'+i.join("")+'" />\n'},t}(),s=function(){function s(s,i){this.config=s,this.x=s.w_caption,this.y=i,this.path=new t(s.signal_style),this.current=0,this.crosses=[],this.strings=[],this.grids=[],this.highlights=[]}return s.transitions=[" "," - 1 4 14"," 2 ~ / ~/"," 3 ` _ _`"," 23`~/_= "," 23`~/_=/"," 23`~/_=`"," 23`~/_X "," 23`~/_=X"],s.transitionLines={" ":[],"~":[[1,1]],_:[[0,0]],"=":[[1,1],[0,0]],X:[[1,0],[0,1]],"`":[[1,0]],"/":[[0,1]],1:[[1,.5]],2:[[.5,1]],3:[[.5,0]],4:[[0,.5]],"-":[[.5,.5]]},s.stateLines=[[],[.5],[1],[0],[0,1]],s.codes=":-~_=/\\X*",s.prototype.ys=function(t){return this.y+(1-t)*this.config.h_line},s.prototype.y0=function(){return this.y+this.config.h_line},s.prototype.y1=function(){return this.y},s.prototype.yz=function(){return this.y+this.config.h_line/2},s.prototype.xh=function(){return this.x+this.config.w_transient/2},s.prototype.xt=function(){return this.x+this.config.w_transient},s.prototype.xr=function(){return this.x+this.config.w_transient+this.config.w_hold},s.prototype.parse=function(t){for(var s,i;""!==t;)(s=/^\s+/.exec(t))||((s=/^\|/.exec(t))?this.grids.push([this.xh(),this.config.grid_style]):(s=/^\[/.exec(t))?(0===this.highlights.length||Array.isArray(this.highlights[this.highlights.length-1]))&&this.highlights.push(this.xh()):(s=/^\]/.exec(t))?this.highlights.length>0&&!Array.isArray(this.highlights[this.highlights.length-1])&&(this.highlights[this.highlights.length-1]=[this.highlights[this.highlights.length-1],this.xh(),this.config.highlight_style]):(i=/^([:\-~_=\/\\X*])/.exec(t))?this.addState(i[1]):(i=/^"(([^"]|"")*)"/.exec(t))?this.addString(i[1].replace(/""/g,'"').replace(/[ ]/g," ")):(i=/([^:\-~_=\/\\X*\|\]\[]+)/.exec(t))&&this.addString(i[1])),t=t.substr(i[0].length,t.length-i[0].length);return this.processStrings()+this.path.svg()},s.prototype.addState=function(t){var i,n;return n=s.codes.indexOf(t),i=this.drawTransition(n),n>4&&(n=4),this.drawState(n),""!==i&&this.crosses.push([this.x,i]),(0===this.current&&0!==n||0!==this.current&&0===n||0===this.crosses.length&&0===n)&&this.crosses.push([this.x,"|"]),this.current=n,this.x=this.xr()},s.prototype.addString=function(t){return this.strings.push([this.crosses.length,t.replace(/^\s+|\s+$/g,"")])},s.prototype.drawTransition=function(t){var i,n,e,h,r;for(i="",e=s.transitions[t].substr(2*this.current,2),n=h=0,r=e.length-1;r>=0?r>=h:h>=r;n=r>=0?++h:--h)i+=this.drawTransitionSub(e[n]);return i},s.prototype.drawTransitionSub=function(t){var i,n,e,h,r;for(i="",r=s.transitionLines[t],e=0,h=r.length;h>e;e++)n=r[e],this.path.draw(this.x,this.ys(n[0]),this.xt(),this.ys(n[1])),n[0]!==n[1]&&(i+=t);return i},s.prototype.drawState=function(t){var i,n,e,h,r;for(h=s.stateLines[t],r=[],n=0,e=h.length;e>n;n++)i=h[n],r.push(this.path.draw(this.xt(),this.ys(i),this.xr(),this.ys(i)));return r},s.prototype.processStrings=function(){var t,s,i,n,e,h,r,o,g,c,a,l,f,u,p,y,x,d,_,w;for(e=[],this.crosses.push([this.x,"|"]),w=this.strings,d=0,_=w.length;_>d;d++)n=w[d],p=this.ys(0),y=this.ys(1),x=this.ys(.5),r=this.crosses[n[0]-1][0],c=r+this.config.w_transient,o=r+this.config.w_transient/2,g=c+this.config.w_hold,a=this.crosses[n[0]][0],u=a+this.config.w_transient,l=a+this.config.w_transient/2,f=u+this.config.w_hold,"?"===n[1]&&(s=["M"+c+","+y+"H"+a],s.push(function(){switch(this.crosses[n[0]][1]){case"|":return"";case"XX":return"L"+l+","+x;case"/":return"H"+u;case"`":return"L"+u+","+p;case"23":return"H"+u+"L"+a+","+x+"L"+u+","+p;case"14":return"L"+u+","+x;case"1":return"L"+u+","+x+"V"+p;case"2":return"H"+u+"L"+a+","+x;case"3":return"V"+x+"L"+u+","+p;case"4":return"H"+u+"V"+x}}.call(this)),s.push("L"+a+","+p+"H"+c),s.push(function(){switch(this.crosses[n[0]-1][1]){case"|":return"";case"XX":return"L"+o+","+x;case"/":return"H"+r;case"`":return"L"+r+","+y;case"23":return"L"+r+","+x;case"14":return"H"+r+"L"+c+","+x+"L"+r+","+y;case"1":return"V"+x+"L"+r+","+y;case"2":return"H"+r+"V"+x;case"3":return"L"+r+","+x+"V{y1}";case"4":return"H"+r+"L"+xt+","+x}}.call(this)),s.push("Z"),e.push('\n<path stroke="none" d="'+s.join("")+'" '+this.config.notcare_style+"/>")),i=n[1].replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),"_<_"===n[1].substr(0,3)?(h=c,t="start",i=i.substring(6)):"_>_"===n[1].substr(0,3)?(h=a,t="end",i=i.substring(6)):(h=(o+l)/2,t="middle"),e.push('<text x="'+h+'" y="'+(this.ys(0)-1.5)+'" text-anchor="'+t+'" '+('font-size="'+this.config.h_line+'" '+this.config.signal_font+">"+i+"</text>\n"));return e.join("")},s}(),i=function(){function t(s){null==s&&(s={}),this.config={},this.setConfig(t.config),this.setConfig(s)}return t.config={scale:1,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"'},t.prototype.setConfig=function(t){var s,i,e;e=[];for(s in t)n.call(t,s)&&(i=t[s],e.push(this.config[s]=i));return e},t.prototype.parse=function(t){var s,i,n,e;for(this.svg=[],this.grids=[],this.highlights=[],this.y=-1,this.x_max=this.config.w_caption,t=t.replace(/^\n+/,""),t=t.replace(/\n+$/,""),e=t.split("\n"),i=0,n=e.length;n>i;i++)s=e[i],this.parseLine(s);return this.processGrids(),this.processHighlights(),this.formatSVG(t)},t.prototype.formatSVG=function(t){var s,i,n;return i=this.config.margin,n=(this.x_max+2*i)*this.config.scale*1.3,s=(this.y+2*i)*this.config.scale*1.3,this.width=n,this.height=s,this.svg='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" \n width="'+n+'px" height="'+s+'px" viewBox="'+-i+" "+-i+" "+(this.x_max+2*i)+" "+(this.y+2*i)+'" version="1.1">\n<![CDATA[\n'+t.replace(/\]\]\>/g,"]]>")+"\n]]>\n<g>\n"+this.svg.join("\n")+"\n</g>\n</svg>"},t.prototype.parseLine=function(t){var s;if("#"!==t[0]){if("@"===t[0]){if(!(s=/^@([^\s]+)[\s]+([^\s].*)$/.exec(t)))throw new SyntaxError("Illegal Line: "+t);return void($.isNumeric(this.config[s[1]])?this.config[s[1]]=Number(s[2]):this.config[s[1]]=s[2])}if("%"===t[0]){if(!(s=/^%(-?[\d\.]+)\s+(-?[\d\.]+)\s+?(.*)$/.exec(t)))throw new SyntaxError("Illegal Line: "+t);return void this.svg.push('<text x="'+s[1]+'" y="'+s[2]+'" text-anchor="middle" '+('font-size="'+this.config.h_line+'" '+this.config.signal_font+">"+s[3]+"</text>"))}if(this.y<0?this.y=0:this.y+=this.config.h_space,t=t.replace(/\s*$/,""),""!==t){if(!(s=/^([^\s]+)[\s]+([^\s].*)$/.exec(t)))throw new SyntaxError("Illegal Line: "+t);return this.formatCaption(s[1]),this.formatTimeline(s[2]),this.y+=this.config.h_line}}},t.prototype.formatCaption=function(t){var s;return s=t.replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),this.svg.push('<text x="'+(this.config.w_caption-5)+'" y="'+(this.y+this.config.h_line-1.5)+'" text-anchor="end" '+('font-size="'+this.config.h_line+'" '+this.config.caption_font+">"+s+"</text>"))},t.prototype.formatTi
meline=function(t){var i,n,e,h,r,o,g,c,a,l;for(e=new s(this.config,this.y),this.svg.push(e.parse(t)),e.x>this.x_max&&(this.x_max=e.x),c=e.grids,h=0,o=c.length;o>h;h++)i=c[h],this.grids.push(i);for(a=e.highlights,l=[],r=0,g=a.length;g>r;r++)n=a[r],l.push(this.highlights.push(n));return l},t.prototype.processGrids=function(){var t,s,i,n,e,h,r;for(i=-this.config.margin/2,t=this.y+this.config.margin/2,h=this.grids,r=[],n=0,e=h.length;e>n;n++)s=h[n],r.push(this.svg.push('<path d="M'+s[0]+","+i+"V"+t+'" '+s[1]+" />"));return r},t.prototype.processHighlights=function(){var t,s,i,n,e,h,r;for(i=-this.config.margin/2,t=this.y+this.config.margin/2,h=this.highlights,r=[],n=0,e=h.length;e>n;n++)s=h[n],Array.isArray(s)?r.push(this.svg.unshift('<path d="M'+s[0]+","+i+"V"+t+"H"+s[1]+"V"+i+'Z" '+s[2]+" />")):r.push(void 0);return r},t.format=function(s){return(new t).parse(s)},t}(),("undefined"!=typeof exports&&null!==exports?exports:this).TimingChart=i}).call(this);
// end of file
Markdown に組み込む†
Markdown に組み込む場合、
```tchart2svg clock _~_~_~_~_~ signal ___~~~~___ ```
と書いた物が
LANG:html <pre><code class="tchart2svg"> clock _~_~_~_~_~ signal ___~~~~___ </code></pre>
と変換される処理系であれば、
LANG:coffeescript $ -> $('code.lang-tchart2svg').each -> code = $(this) code.parent().replaceWith (new TimingChart()).parse(code.html())
すなわち、
LANG:html <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> <script src="tchart.min.js"></script> <script> $(function(){ $('code.lang-tchart2svg').each(function(){ var code = $(this); code.parent().replaceWith( TimingChart.format(code.html()) ); }); }); </script>
をレイアウトファイルの <head> に組み込めば、自然な文法でタイミングチャートを記述できるようになります。