「第4回 エンジニアのためのプレゼン技術研究会」の参加メモ

第4回 エンジニアのためのプレゼン技術研究会「みんなの自由研究発表」と「前佛さんのスライドの作り方」に参加してきました。

プレゼン用の図を自動生成する話

今回のテーマは「みんなの自由研究発表」で、各人の自由研究の成果を持ち時間5分で発表するというものでした。自分も「プレゼン用の図を自動生成する話」というタイトルで発表を行いました。

Webブラウザ上で動作するmermaidAPI.jsというJavaScriptライブラリがあり、これを利用して簡単なテキストから動的に図を生成する、というネタでの発表内容でした。

サンプルプログラムは以下のURLから試してみることができます。

mermaidAPI.jsのサンプルプログラム使用手順

発表時間が5分ということもあり、当日の発表ではデモを行えなかったので、上記のサンプルプログラムを使用した簡単な作業フローを紹介します。

まずはサンプルプログラムでmermaidの記法に沿ったテキストを入力し、図を作成します。図は自動で生成(レンダリング)されます。

例として、アニメ「プリパラ」の人物相関図を作成してみます。以下のような感じですね。

f:id:furandon_pig:20151012174646p:plain

mermaidAPI.jsはSVGを生成するので、Inkscape等のSVGが編集可能なツールで図の修正や微調整が行えます。ただし、mermaidAPI.jsが生成するSVGは、テキスト表示用に独自のSVGタグを使っていたりするので、そのままではInkscapeで表示できないものがあります。上記サンプルプログラムではInkscapeで表示できるよう、生成されたSVGを一部修正しています。

f:id:furandon_pig:20151012174715p:plain

Inkscapeで表示すると以下のような感じになります。図中の矢印は直線で構成されていますが、パスの中間点を削除すると滑らかな曲線になるので、ここは手動で中間点を削除します。他にもパスの端点を矢印の形に変更したりしています。

f:id:furandon_pig:20151012234535p:plain

あとはEPS形式で保存するだけなのですが、その前に画像サイズを調整します。[ファイル]-[ドキュメントのプロパティ(D)...]を選択し、「ページ」タブの中にある「ページサイズを描画全体または選択オブジェクトに合わせる(R)」ボタンをクリックします。

f:id:furandon_pig:20151012235420p:plain

ESPで保存した画像は、KeynotePowerPointなどに挿入して利用します。Keynoteの場合は、メニューから[挿入]-[選択...]でEPSファイルを挿入できます。

f:id:furandon_pig:20151012174725p:plain

まとめ

第4回 エンジニアのためのプレゼン技術研究会で発表した内容の補足説明をまとめてみました。プレゼン技術に関するノウハウはスライドの作り方・構成方法、発表時のハマり所等、以外と範囲が広いです。今後もプレゼン技術研究会でノウハウを集めてゆければと思います。

MagicPointのスライドをPDFに変換する際にハマった話

MagicPointのスライドをPDFに変換する際にハマった話

私のスライド作成環境がMagicPointという一風変わった環境のせいもあるのですが、勉強会での発表スライドをWeb上で公開する際、少し古めかしい方法(スライド画像を単に列挙するだけ)になっていました。

MagicPointのスライドはPDFに変換でき、PDFであればSlideShareにアップロードできるようなので、作業手順をまとめてみました。

MagicPointとは?

MagicPointはテキストベースのプレゼンテーションツールです。

例えば以下のような感じでスライドを作成します(これで一枚のスライドになります)。テキストエディタさえあればスライドが作成できる点はなかなか魅力的です。

%page

スライドタイトル

    BSDの種類
        FreeBSD
        NetBSD
        OpenBSD

MagicPointによるスライドのサンプル

スライドのサンプルを以下に示します。スライド中でも説明していますが、epsファイルの挿入が可能です。

PDFファイルへの変換

MagicPointのコマンド(mgpコマンド)にはHTMLにエクスポートするオプションがあり、今まではこれを利用していました。mgp2psというコマンドの存在を知り、PS→PDFに変換できるのではと思い、手順を調べてみました。

その際、かなり色々とハマってしまったので、自分への備忘録も兼ねて手順をまとめてみます。

mgpコマンドで画面上にスライドを表示する場合は問題ないのですが、背景色と前景色を指定し、かつepsファイルを取り込んでいるスライドをPS,PDFに変換する場合、epsファイル側に一工夫入れないとダメでした。

具体的な問題としては、挿入したepsの背景色でスライドの文字が上書き(塗りつぶされる)という現象が発生します。白背景に黒色の前景色だと問題ないかもしれませんが、白と黒色だけのスライドは味気ないので何とか対応方法を調べてみました。

今回は以下のeps出力ケースの場合での対応方法となります。また、スライドの背景色、前景色はそれぞれDarkBlue,snowという前提です(前述のスライドサンプル参照)。

  • tgifが出力したeps
  • Inkscapeが出力したeps
  • TeXから生成したeps

tgifが出力したeps

tgifで背景色等を変更せずに図を描いた場合、背景色は白、前景色は黒になります。このままではスライドの色と異なるため、見辛いものとなります(青色の背景に黒色の組み合わせは見辛いです)。

そこでtgifが出力したeps内の背景・前景色を以下のコマンドで修正します。生成されたepsではPostScriptのsetrgbcolorをRGというコマンドで定義しなおしている(C言語のdefineのような使い方)ので、この部分について色を置き換えます。

$ tgif -print -stdout -eps foo.obj > foo.tmp.eps
$ # 青背景(0 0 1 RG)に白描画(1 1 1 RG)に修正している
$ cat foo.tmp.eps \
    | sed -e "s/            0 SG$/            1 1 1 RG/g" \
          -e "s/L CP$/L CP 1 1 1 RG/g" \
          -e "s/ L$/ L 1 1 1 RG/g" \
          -e "s/   0 SG$/   1 1 1 RG/g" \
          -e "s/L CP 1 SG F/L CP 0 0 1 RG F/g" \
    > foo.eps

RGコマンド(setrgbcolor)は"R G B setrgbcolor"となっており、色成分は0.0から1.0までの間で指定するようです。

Inkscapeが出力したeps

Inkscapeについては、epsで保存し直す前のSVGファイルに対して修正を加えます。テキストとストロークの色情報はfill,stroke要素で定義されています。"fill:#RRGGBB"のような定義になっているので、RGBの部分を置き換えます。

$ cat foo.svg \
    | sed -e "s/fill:\#000000/fill:\#FFFAFA/g" \
          -e "s/stroke:\#000000/stroke:\#FFFAFA/g" \
    > foo2.svg

foo2.svgInkscapeで開き、epsで保存し直せば完了です。MagicPointにInkscapeで作成したepsを取り込む場合、拡大・縮小で線が潰れてしまうことがあるので、線の太さを3px等にしておくと良いです。

TeXから生成したeps

TeXから生成したepsの場合は、対応方法が少しややこしいです。手始めにcolorマクロを使用し、スライドの背景色と前景色に合わせたepsを出力します。実際はMagicPointの色はshowrgbコマンドで出力される色名になっており、TeXのcolorマクロで指定可能な色名と異なります。できるだけ近い色を指定してください。今回はDarkBlue(スライド)に対しblue(TeX)としています。

\documentclass{article}
\pagestyle{empty}

\usepackage{color}

\begin{document}
%% \pagecolor{blue}
\color{white}

...TeXの本文...
\end{document}

以下の手順でTeXからepsを生成します。TeXのcolorマクロを使用していると、生成されたepsには背景色を描画(塗りつぶす)する命令が含まれるようです。"f{P fill}..."の部分が当該箇所なので、この部分の描画処理を無効化します。

$ platex foo.tex
$ dvips foo.dvi
$ ps2eps -f foo.ps
$ eps2eps foo.eps foo.tmp.eps
$ # 背景色の描画を無効化する
$ cat foo.tmp.eps \
    | sed -e "s/^\/f{P fill}.*$/\/f{}!/" \
    > output.eps

補足

TeXのlistings環境でソースコードを見やすく整形できます。これをepsに変換してMagicPointのスライドで用いると見栄えの良いスライドに近づきます。

listings環境を提供するlistings.styは日本語に対応していないようで、有志の方が日本語対応したjlisting.styを提供しています。

参考までに、FreeBSD-10の環境でのjlisting.styのインストール手順を示します。

$ gzip -d jlisting.sty.bz2
$ sudo mkdir /usr/local/share/texmf-dist/tex/latex/jlisting
$ sudo mv jlisting.sty /usr/local/share/texmf-dist/tex/latex/jlisting/
$ sudo chown root:wheel /usr/local/share/texmf-dist/tex/latex/jlisting/jlisting.sty
$ sudo chmod go+rx /usr/local/share/texmf-dist/tex/latex/jlisting/
$ sudo chmod go+r /usr/local/share/texmf-dist/tex/latex/jlisting/jlisting.sty
$ sudo mktexlsr

まとめ

MagicPointのスライドをPDFに変換する手順をまとめてみました。背景色を指定しepsを含むスライドをPS,PDFに変換する場合はいろいろとハマり所があり苦労しました。しかし一通りの手順は把握できたので、これでMagicPointを活用する場面が増えそうです。

ご注文はうさぎですか?第7羽からインスパイアを受けてHTML5で「ごちうさパズル」を作ってみた

ご注文はうさぎですか?第7羽からインスパイアを受けてHTML5で「ごちうさパズル」を作ってみた

ごちうさパズル

ご注文はうさぎですか?の第7羽Call Me Sister.のストーリー中で、ココアがパズルを完成させてしまうというエピソードがありました。

このエピソードからインスパイアを受け、「ごちうさパズル」を作ってみました。

あそびかた

選択した分割数(ピースの数)に応じて画像がパズルのピース毎にシャッフルされます。最大で81ピースまで選べるようにしてあります。ごちうさ本編中では4000ピースのパズルだったので、81ピースはだいぶ少ないですが、なかなか没頭できます。

解くのが面倒になった時は"RESET"ボタンを押すとパズルが元に戻ります。

ごちうさパズルの実装

パズルのエピソードを観た時、HTML5で実装できそうだと思い立ったのですが、実装で地味にハマるところが多かったです。

パズルのピースをシャッフルする際の乱数データの取得方法

パズルのピースをシャッフルする際、乱数を使ってデータをシャッフルしています。パズルのピースは重複しないため、値が重複しないように乱数値をあつめる必要があります。かなり昔のI/Oというコンピュータ雑誌に「重複しない乱数列を得る方法」的な投稿コラムがあったのですが、当時はその原理を理解できませんでした。

が、実際には難しい話ではなく、以下の方法で実現できます。

  • あらかじめ配列に値を入れておく(等差数列でOK)
  • 乱数の値を取得し、配列のインデックスとする(乱数の範囲は0〜(配列のサイズ-1))
  • インデックスが指す値を配列の末尾の値と入れ替える
  • 乱数の範囲を1減らす
  • 乱数の範囲が0より大きい間、処理を繰り返す

JavaScriptによるサンプルは以下のようになります。

var value_list = new Array(10000);

// あらかじめ配列に値を入れておく
for (var i = 0; i < value_list.length; i++) {
    value_list[i] = i;
}

var range = value_list.length - 1;
do {
    // 乱数の値を取得し、配列のインデックスとする
    var index = ~~(Math.random() * range);

    // インデックスが指す値を配列の末尾の値と入れ替える
    var tmp = value_list[index];
    value_list[index] = value_list[range];
    value_list[range] = tmp;

    // 乱数の範囲を減らす
    range--;
} while (0 < range);  // 乱数の範囲が0より大きい間、処理を繰り返す

for (var i = 0; i < value_list.length; i++) {
    console.log(value_list[i]);
}

上記のサンプルを試してみます。配列の中身を一行毎出力しており、単に行数をカウントした値とsort,uniqで重複する値を除去した行数の値が同じであることから、重複しない値であることが分かります。動作環境はNetBSD-6.1-i386です。

$ node sample.js | wc -l
   10000
$ node sample.js | sort | uniq | wc -l
   10000

値もシャッフルされています。

$ node sample.js | head
2537
6218
7624
7580
692
4295
2508
6329
1150
4781

Pemtium M 1GHz,データの件数1万件の場合で0.4秒程度の処理時間です。

$ time (node sample.js 2>&1 > /dev/null)

real    0m0.499s
user    0m0.462s
sys     0m0.020s

$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 9
model name      : Intel(R) Pentium(R) M processor 1000MHz
stepping        : 5
cpu MHz         : 996.80
fdiv_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr mce cx8 apic sep mtrr pge mca cmov pat clflush dts acpi mmx fxsr sse sse2 tm

参考までに、取得した乱数値が重複していないか逐一確認する処理方法の場合と比較してみます。30秒程度かかっており、こちらはオススメできない方法となっています。

$ cat sample-bad.js
var value_list = new Array(10000);

for (var i = 0; i < value_list.length; i++) {
    var index;

    // 配列の値が初期値(-1)でない間繰り返す
    do {
        // 乱数の値を取得し、配列のインデックスとする
        index = ~~(Math.random() * value_list.length);
    } while (value_list.indexOf(index) != -1);

    value_list[i] = index;
}

for (var i = 0; i < value_list.length; i++) {
    console.log(value_list[i]);
}
$ time (node sample-bad.js 2>&1 > /dev/null)

real    0m31.960s
user    0m30.481s
sys     0m0.040s

HTML5 Canvasと画像サイズの関係

ごちうさパズルではピースの数を選択できるようにしています。そのため、画像の幅・高さと1ピースあたりの幅・高さが割りきれないことがあり、ピースを並べて行った結果、Canvasや画像のサイズをわずかにはみ出してしまうことがあります。

今回のケースで考えると、画像サイズは600x600で、パズルのピースはNxNとしており、49pcs(7x7)と81pcs(9x9)の場合に幅・高さが割りきれない値になります。当初、単純に四捨五入(Math.round())する方法をとったのですが、Firefoxでは動作するもSafariではおかしなピースが描画されるという、ブラウザの挙動の違いにハマってしまいました。

$ node
> 600/2
300
> 600/3
200
> 600/4
150
> 600/5
120
> 600/6
100
> 600/7
85.71428571428571
> 600/8
75
> 600/9
66.66666666666667

結果、割りきれないケースの場合は、扱う画像の範囲を少し小さく扱うことで対応しました。

> 600/9
66.66666666666667
> 66*9
594        /* 594pxを画像の幅・高さとする */

Canvas.strokeStyle()の設定

これは私がCanvasの仕様を把握できていなかっただけなのですが、Canvas.strokeStyle()などでストロークの色を変更すると、既に描画したストロークの色も一緒に変更されるようです(Firefoxでしか確認してないので、他のブラウザだと違うかもしれません)。描画したデータについても、内部的には状態を保持しているようです。

まとめ

ご注文はうさぎですか?第7羽のエピソードを元に「ごちうさパズル」を作ってみました。HTML Canvasまわりではブラウザ毎の挙動の微妙な違いにハマってしまいました。Canvasまわりは私の理解が追いついていないところもあり、もう少し調べてみようと思います。