タイミングチャート清書サービス のバックアップ差分(No.3)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* 概要 [#td75a95f]

[[こちらで紹介したタイミングチャート清書スクリプト>ソフトウェア/tchart.rb]]をオンラインで使えるようにしました。
[[こちらで紹介したタイミングチャート清書スクリプト>ソフトウェア/tchart.rb]] と同等の物を
javascript で実装し、オンラインで使えるようにしました。

- コードを書けばほぼリアルタイムで清書結果を確認できます
- 清書したタイミングチャートは SVG または PNG 形式でダウンロードできます

>  &ref(screen-capture1.png,,66%);

* 設置アドレス [#s578cb5d]

こちらです:~
http://dora.bk.tsukuba.ac.jp/~takeuchi/tchart.php
こちらです: http://output.jsbin.com/zacubomibi

* ソースコード [#sabe6496]
- [[チャート記述文法はこちらをご覧下さい。>http://dora.bk.tsukuba.ac.jp/~takeuchi/?%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%2Ftchart.rb#ya5f8236]]

 LANG:php(linenumber)
 <?php 
* ソースコード [#sc7a574d]

 LANG:html(linenumber)
 <!DOCTYPE html>
 <html>
 <head>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
 <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
   <script src="https://rawgit.com/eligrey/FileSaver.js/master/FileSaver.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>JS Bin</title>
 </head>
 <body>
   <h1>Timing Chart Formatter</h1>
   <h2>Source</h2>
   <textarea id="source" cols="60" rows="10">clock	_~_~_~_~_~_~_~_~_~_~_
 data	=?====X=DATA========X=?====
 valid	_____~~~~~~~~~~______
 ready	_____________[~~]______</textarea>
   <h2>Result</h2>
   <input type="button" value="Download as SVG" id="as_svg" />
   <input type="button" value="Download as PNG" id="as_png" /> Background: <input id="background" value="white" style="width:60px" />
   <br />
   <br />
   <div id="result"></div>
 </g>
 </svg>
 </div>
 </body>
 </html>

 LANG:coffeescript(linenumber)
 class SvgPath
   constructor: (style)->
     @segments = []
     @style = style
 
   function generate_svg($source)
   {
     $descriptorspec = array(
        0 => array("pipe", "r"),             // stdin
        1 => array("pipe", "w"),             // stdout
        2 => array("file", "/dev/null", "a") // stderr
     );
     $process = proc_open('/usr/local/bin/tchart.rb', $descriptorspec, $pipes);
   draw: (x1, y1, x2, y2)->
     @segments.push([x1,y1,x2,y2])
 
     if (is_resource($process)) {
       fwrite($pipes[0], $source);
       fclose($pipes[0]);
   svg: ->
     path = ("M#{s[0]},#{s[1]}L#{s[2]},#{s[3]}" for s in @segments).join("")
     """<path #{@style} d="#{path}" />\n"""
 
       return stream_get_contents($pipes[1]);
       fclose($pipes[1]);
 class TimeLine
   # : - ~ _ = 
   @transitions = [
     '          ', # :
     '  - 1 4 14', # -
     '  2 ~ / ~/', # ~
     '  3 ` _ _`', # _
     '  23`~/_= ', # =
     '  23`~/_=/', # /
     '  23`~/_=`', # \
     '  23`~/_X ', # X
     '  23`~/_=X', # *
   ]
 
       $return_value = proc_close($process);
     }
     return "";
   }
   @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]]
 
   function bad_referer()
   {
     $referer = $_SERVER["HTTP_REFERER"];
     $url = parse_url($referer);
     return $url['host'] != $_SERVER["SERVER_NAME"];
   }
   #                :  -     ~   _   =
   @stateLines = [[],[0.5],[1],[0],[0,1]]
 
   if(@$_POST['source'] && @$_POST['format']=='svg'){
     if(bad_referer()) exit;
   #
   @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])
       else if matches = /([^:\-~_=\/\\X*\|\]\[]+)/.exec(line)
         @addString(matches[1])
       line = line.substr(matches[0].length, line.length-matches[0].length)
     
     @processStrings() + @path.svg()
 
 #    header('Content-type: image/svg+xml');
     header('Content-Type: application/force-download');
     header('Content-Disposition: attachment; filename="tchart.svg"');
     echo generate_svg($_POST['source']);
     exit;
   }
   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)
       @crosses.push([@x, '|'])
     @current = s
     @x = @xr()
 
   if(@$_POST['source'] && @$_POST['format']=='png'){
     if(bad_referer()) exit;
   addString: (s)->
     @strings.push [@crosses.length, s.replace(/^\s+|\s+$/g, '')]
 
     $svg = generate_svg($_POST['source']);
   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
 
     $tempname = tempnam('/tmp', 'tchart');
     $svgname = $tempname . '.svg';
     $pngname = $tempname . '.png';
   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
 
     $svgfile = fopen($svgname, 'w');
     fwrite($svgfile, $svg);
     fclose($svgfile);
     system("/usr/bin/convert ${svgname} ${pngname}");
     unlink($svgname);
       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}/>"""
 
 #    header("Content-Type: image/png");
     header('Content-Type: application/force-download');
     header('Content-Disposition: attachment; filename="tchart.png"');
     readfile($pngname);
     unlink($pngname);
     exit;
       sanitized = string[1].replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
       svg.push """<text x="#{(x1h + x2h)/2.0}" y="#{@ys(0)-1.5}" text-anchor="middle" """ +
                """font-size="#{@config.h_line}" #{@config.signal_font}>#{sanitized}</text>\n"""
 
 #    下記はなぜかうまく動かない
 #    $im = new Imagick();
 #    $im->readImageBlob($svg, 'tchart.svg');
 #    $im->setImageFormat("png24");
 #    header("Content-Type: image/png");
 #    header('Content-Disposition: attachment; filename="tchart.png"');
 #    echo $im;
 #    exit;
   }
 ?>
     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"'
 
 <!DOCTYPE html>
 <html>
   <head>
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <title>Timing chart formatter</title>
     <meta charset="utf-8">
     <meta name="description" content="Timing chart formatter">
     <meta name="author" content="Osamu Takeuchi &lt;osamu@big.jp&gt;">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <!-- link rel="stylesheet" href="" -->
     <!--&#91;if lt IE 9&#93;>
     <script src="//cdn.jsdelivr.net/html5shiv/3.7.2/html5shiv.min.js"></script>
     <script src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
     <!&#91;endif&#93;-->
     <!-- link rel="shortcut icon" href="" -->
   </head>
   <body>
     <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
   constructor: (config= {})->
     @config= {}
     @setConfig(TimingChart.config)
     @setConfig(config)
 
     <h1>Timing chart formatter</h1>
   setConfig: (config)->
     @config[k]= v for own k,v of config
 
     <h2>Source Code</h2>
   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)
 
     <a href="http://dora.bk.tsukuba.ac.jp/~takeuchi/?%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%2Ftchart.rb">View sytax of the source text.</a><br>
     <br>
   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 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, ']]&gt;')}
     ]]>
     <g>
     #{@svg.join("\n")}
     </g>
     </svg>
     """
 
     <form name="form" method="post" target="_blank">
     <input type="hidden" name="format" id="format" value="svg" />
     <textarea id="source" name="source" cols="120" rows="20"># comment
 clock	_~_~_~_~_~_~_~_~_~_~_
 data	=?====X=DATA========X=?====
 valid	_____~~~~~~~~~~______
 ready	_____________[~~]______
 </textarea>
     </form>
     <input type="button" id="reload" value="Reload">
     <span id="indicator"></span><br />
   parseLine: (line)->
     return if line[0] == '#'  #### comment
 
     <h2>Result</h2>
     <input type="button" id="as_svg" value="Download as svg" />
     <input type="button" id="as_png" value="Download as png" /><br />
     <div id="result"></div>
   </body>
     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
 
 <script>
     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
 
 $(function(){
     line= line.replace(/\s*$/, '')
     return if line==''        #### empty line
 
   var reload = function(source) {
     if (source != "") {
       $("#indicator").html("*");
       $.post("tchart.php", {
           "source": source,
           "format": "svg"
         }, 
         function(data, status) {
           if (status=="success") {
             $("#result").html(data);
           } else {
             $("#result").html(status);
           }
           $("#indicator").html("");
         },
         "text"
       );
     }
   }
     if !(matches = /^([^\s]+)[\s]+([^\s].*)$/.exec(line))
       throw new SyntaxError("Illegal Line: #{line}")
     
     @formatCaption(matches[1])
     @formatTimeline(matches[2])
     @y+= @config.h_line
 
   $('#reload').on('click', function(e){
     source = $("#source").val();
     reload(source);
   });
   formatCaption: (caption)->
     sanitized = caption.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
     @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>"""
     )
 
   var lastSource = '';
   setInterval(function(){
     source = $("#source").val();
     if (source!=lastSource)
       reload(source);
     lastSource = source;
   },1000);
   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]} />"""
 
   $('#as_svg').on('click', function(e){
     $('#format').val('svg');
     document.form.submit();
   });
 download= (dataUrl, filename)->
   form = $('<form>')
   form.action = dataUrl
   form.target = '_blank'
   form.submit()
                   
 lastSource = ''
 update = ->
   source = $('#source').val()
   return if lastSource==source
   lastSource = source
   
   tchart= new TimingChart()
   svg= tchart.parse(source)
   $('#result').html(svg)
 
   $('#as_png').on('click', function(e){
     $('#format').val('png');
     document.form.submit();
   });
 $ ->
   $('#as_svg').on 'click', ->
     update()
     blob = new Blob([$('#result').html()], {type: "image/svg+xml"});
     saveAs(blob, 'timing-chart.svg')
 
 });
   $('#as_png').on 'click', ->
     source = $('#source').val()
     tchart= new TimingChart()
     svg= tchart.parse(source)
 
 </script>
     canvas = document.createElement('canvas')
     canvas.width = tchart.width
     canvas.height = tchart.height
 
 </html>

     # 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)
       url = canvas.toDataURL('image/png')
       link = document.createElement('a')
       link.href = url
       link.download = 'timing-chart.png'
       link.click()
 
     img.src = "data:image/svg+xml;base64," + btoa(svg)
 
   setInterval(update, 100)
* コメント・質問 [#m9652e62]

#article_kcaptcha


Counter: 66295 (from 2010/06/03), today: 6, yesterday: 0