« 2012年5月 | トップページ | 2012年7月 »

2012年6月

2012年6月26日 (火)

数式表示 MathJax -4-

MathJax の件ですが、取り敢えず、多重起動の問題は解決しました。以下が正しいコードです。

<script>
<!--
(function (){
    if (!document.defMathJax){
        var s = '<script type="text/x-mathjax-config">\n';
        s += 'MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]] } });\n';
        s += '</script>\n';
        s += '<script type="text/javascript" ';
        s += 'src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">\n';
        //s += 'document.defMathJax = true;\n'; これは間違い
        s += '</script>\n';
        s += '<meta http-equiv="X-UA-Compatible" CONTENT="IE=EmulateIE7" />\n';
        document.write(s);
        document.close();
        document.defMathJax = true; // これが正しい
    }
})();
// -->
</script>
色々いじっている内に、上記間違いを見落としていました。

これで、多重起動の問題は解決したのですが、タブレットでの不具合は相変わらず未解決です。さっぱり分かりません。

数式表示 MathJax -3-

今、タブレットで数式の表示がどうなっているか確認したら、MathJax が機能していませんでした。MathJax は実行しているようなのですが、数式が整形されません。試しに MathJaxの使い方 にアクセスしたら、ここはタブレットでも正常に数式が表示されます。

次に、ココログのサブタイトル(キャッチフレーズ)の欄に再度オマジナイを置いてからタブレットでアクセスしましたが、ダメでした。この時、今までよりも MathJax の処理に時間が掛かっているようで嫌な予感がしたので、-2- で取り上げたスクリプトを1つのファイルに複数書いて実験したら、・・・がびーん!! document.defMathJax が役に立っていません。スクリプトの数だけ MathJax を起動しています。何故なんだ??(取り敢えず、MathJax が複数回起動しても問題は無いようなので放っておきます。1ページのオマジナイの数は高々2~3回でしょうから)

何故、このブログでは MathJax が機能しないのでしょうか? サイト「MathJaxの使い方」では機能しているのに! オマジナイは <head> 領域に置かないとダメなのでしょうか? PCでは大丈夫なのに。

数式表示 MathJax -2-

MathJax のオマジナイをココログのサブタイトル(キャッチフレーズ)の欄に入れることで数式を表示できるようになった訳ですが、場違いな箇所に書いているのが気に入りません。

また、次の理由からもその方法は好ましくありません。この「SpaceLike のブログ」で数式を使う記事は今までも、これからもほとんど無いでしょう。たまにしか必要としない時のために、ページを開く度にオマジナイを実行するのはトラフィックの無駄です。

そこで、数式を記述する時だけオマジナイを実行することにしました。不慣れなために色々と試行錯誤をしながら辿り着いたのが次のコードです。これを記事の先頭に記述すれば MathJax で綺麗な数式を表示することが出来ます(HTML の記述)。

<script>
<!--
(function (){
    if (!document.defMathJax){
        var s = '<script type="text/x-mathjax-config">\n';
        s += 'MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]] } });\n';
        s += '</script>\n';
        s += '<script type="text/javascript" ';
        s += 'src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">\n';
        s += 'document.defMathJax = true;\n';
        s += '</script>\n';
        s += '<meta http-equiv="X-UA-Compatible" CONTENT="IE=EmulateIE7" />\n';
        document.write(s);
        document.close();
    }
})();
// -->
</script>

基本的な考えは、document.defMathJax が定義されていれば既にオマジナイは実行されているとみなすが、document.defMathJax が未定義ならばオマジナイを実行するというものです。ブログには1つのページに複数の記事が表示されることが有りますが、上記コードは同一ページに複数回記述されていても問題は有りません。つまり、記事毎に任意に上記コードを記述できる訳です。(追記: document.defMathJax が排他の役を果たしていません。上記コードの度に MathJax が起動されるようです。数式表示 MathJax -3-, 数式表示 MathJax -4- を参照してください)

このコードが出来るまでには幾つかの失敗が有りました。順不同で挙げて行きます。

まず1つは、document.defMathJax ではなくて defMathJax を使ったために defMathJax が未定義だというエラーが発生したことです。このエラーを回避するために defMathJaxdocument のメンバーとしました。

2つ目は、 <script> 領域の内側を <!--// --> でコメントアウトしなかったことです。このせいでスクリプト内で使われている <> が HTML コードとして解釈されてしまいました。コメントアウトはスクリプト非対応のブラウザの為に行うというのは知っていましたが、スクリプト非対応なら、どうせ MathJax も使えないのだから、まぁいいかと手抜きしていました。しかし、スクリプトのコメントアウトはもっと本質的な処理だった訳です。

3つ目は、

    document.write(s);

    document.all.***.innerHTML = s;
    ・・・
</script>
<div id="***">
</div>
にしていたことです(「***」は有効な文字列を使用)。JavaScript のリファレンスを調べて innerHTML が有ったので、「これかな?」と思って使ってみたのですがダメでした。もっと単純に document.write() が有ったのです。

他にも愚かな間違いを重ねて、やっと上記コードが出来ました。そして、必要な時だけオマジナイを実行して綺麗な数式を表示できるようになりました。

2012年6月25日 (月)

数式表示 MathJax -1-

先の記事で数式を扱いましたが、これは MathJax というものを使っています。MathJax は、\(\rm\LaTeX\) で書かれた数式を整形する JavaScript です。次のオマジナイを <head> 領域に記述しておけば、\(\rm\LaTeX\) の数式を整形してくれます。

<!-- オマジナイ -->
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]] } });
</script>
<script type="text/javascript"
  src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">
</script>
<meta http-equiv="X-UA-Compatible" CONTENT="IE=EmulateIE7" />
詳しくは、次のサイトを参照して下さい。
MathJaxの使い方

この MathJax を使うと、Σを使ったこんな式
\[ \sum_{k=1}^{n}n = \frac{n(n+1)}{2} \] も、積分や括弧や指数を使ったこんな式
\[ \left\| F \right\|_2 = \left(\int_{0}^{2\pi}\left| F(e^{i\omega})\right|^{2}d\omega\right)^{\frac{1}{2}} \] も美しく表示することが可能です。

さて、この MathJax をココログで使おうとしたのですが、上記オマジナイを <head> 領域に入れる方法が分かりません。たぶん方法は無いと思います(有れば教えてほしい)。因みに、ココログの種類はベーシックです。

そこで、ご法度破りの手を使うことにしました。それは、ココログのサブタイトル(キャッチフレーズ)欄に上記コードを記述することです。この欄は、ページの <body> 領域に配置されているのですが、ブログ内のどのページを開いても必ず存在し、そして1つより多くは存在しません。実質的に <head> 領域のようなものです。この欄に記述することで MathJax を利用出来るようになりました。

なお、このサブタイトル(キャッチフレーズ)欄を利用するというアイデアは、アイコンの設定を解説したブログ記事
第32回:自分専用のfavicon(ファビコン)を付けてみよう
から頂きました。

ところで、この方法がなぜご法度破りなのか。それは、サブタイトル(キャッチフレーズ)の欄に、それとは関係無いものを入れたからです。本来の使い方では無いはずです。それも、今回はスクリプトを入れるというとんでもないことをしています。そこで、次回は別の方法について書くことにします。現時点では、その別方法で MathJax を利用しています。

アニメーション機能の実装、その前に

FlatTable にアニメーション機能を実装する段階になりましたが、コーディングの前に解決しておかなければならない問題が有ります。それは球の運動です。FlatTable のアニメーションは配置図に基づいて実行されるのですが、その配置図には球の速度に関する直接の記述は有りません。アニメーションの為には、配置図から球の速度を導出しなければなりません。それについて説明します。

球の速度の決定には次の2つの力学要素を用います。

  1. 停止状態からの逆算
  2. 衝突からの逆算

停止状態からの逆算

説明図 転がっている球にはラシャの抵抗が一定の力で働いています。その時の球の速度と移動距離は次のようになっています。
\[\left\{ \begin{array}{rcl} v & = & -\alpha t + v_{0} \\ \ell & = & -\frac{1}{2}\alpha t^{2} + v_{0}t \end{array} \right.\] ここに、\(\alpha\) はラシャの抵抗による加速度(減速度)、\(v_{0}\) は球の初速度、\(\ell\) は球の移動距離です。この式の中で配置図から設定できるのは、球の移動距離です。さらに終速度(\(v_{c}\))も既知として方程式を解くと、初速度
\[ v_{0} = \sqrt{v_{c}^{2} + 2\alpha \ell} \tag{1} \] が求まります。特に終速度が0即ち球が停止する場合の初速度は、 \[ v_{0} = \sqrt{2\alpha \ell} \] となり、\(v_{c}\) の仮定は必要有りません。

衝突からの逆算

Collision 図のように手球が \(v_{0}\) の速度で的球に当たり、手球の速度は \(v_{1}\) 、的球の速度は \(u_{1}\) になったとします。この時、運動量保存およびエネルギー保存より、次の関係が成り立っています(質量はどれも同じなので省略しています)。
\[\left\{ \begin{array}{rcl} v_{0} & = & u_{1}\cos \alpha + v_{1}\cos \beta \\ 0 & = & u_{1}\sin \alpha + v_{1}\sin \beta \\ v_{0}^{2} & = & u_{1}^{2} + v_{1}^{2} \end{array} \right.\] この連立方程式を解いて行くと、散乱角に
\[ \alpha - \beta = \pm 90° \] の関係が有ることが分かります。それを用いて衝突前の手球の速度
\[ v_{0} = u_{1}\sqrt{1+\tan^{2}\alpha} \] が求まります。ただし、これは弾性衝突の場合であって、実際は \(1\) より小さな反発係数 \(k\) を考慮する必要が有ります。従って、衝突前の速度は
\[ v_{0} = \frac{1}{k} u_{1}\sqrt{1+\tan^{2}\alpha} \tag{2} \] となります。これで、衝突された球の速度から衝突した球の速度が求まりました。

2012年6月20日 (水)

経路線を背面に変更

FlatTable にグリッド表示機能を実装した時、どのグループに appendChild() するかによってZ軸順序に違いが生じることが分かったわけですが、このことを利用してある不具合を解消しました。

配置図例 右図は不具合解消前の FlatTable で作成した経路です。幅広の経路線が球を覆うように描かれています。つまり、経路線の方が球よりも前面に生成されている訳です。そのため、球をクリックしようとしても経路線に邪魔されて失敗に終わります。これは、経路線 baseBall.path がページ curPageappendChild() されていることに関係しています。

    curPage.appendChild(baseBall.path);
ここに、curPageballscloneNode() したものです。そして balls は的球および手球の SVG データの集まりです。
    <g id="balls">
        的球および手球
    </g>
経路線は球よりも後で appendChild() されているので、球よりも前面に表示されるのです。

そこで、この不具合を解消するために、経路線を balls 内の初めの方に appendChild() することにしました。即ち、balls を次のように変更し、

    <g id="balls">
        <g />
        的球および手球
    </g>
この <g /> の部分に経路線を appendChild() することにしました。
    curPage.childNodes[1].appendChild(baseBall.path);
childNodes[1] が <g /> に当たります。

上図の経路を、不具合解消済みの FlatTable で作成したものが下図です。経路線は15番の球に被さっていません。



この図は FlatTable を実際に表示しているものです。したがって、この図の中で配置を操作することが出来ます。(詳しい操作方法を知るには、図中のボタン[ Table ]をクリックして出るメニューの help をクリックしてください。別タブに操作説明が表示されます。)

- 追記 -

私のPCに入っているIEは Ver8 で SVG には対応していないので、上記 FlatTable がどのように表示されているか確認のため、このページをIEで表示してみました。ところが、いつの間にか Ver9 になっており、SVG が表示されます。ただし、普段 Chrome で表示しているのとはかなり違っています。上記 FlatTable は <iframe> を使って表示しているのですが、Chrome では <iframe> の枠いっぱいに表示しているのに対して、IE9では <iframe> 枠は同じなのに FlatTable の画像が小さくなっています。これは困ったことになりました。

- 追記 2 -

IE9で FlatTable が小さくなるという問題は改善しました。しかし、 Chrome 以外のブラウザは表示を確認していないので、IE9と同様の問題が出ているかも知れません。

2012年6月15日 (金)

FlatTable 公開

SVG 版 FlatTable で予定している大きな機能は次の2つです。

  • 配置図作成・表示
  • 配置図のアニメーション
このうち「配置図作成・表示」は(多少の不備は有るでしょうが、一応)実装を終了しました。そこで、SVG 版 FlatTable を公開することにします。
FlatTable プール用(SVG 版)
(追記:サフィックスを .html に変更; 追記2:サフィックスを .svg に変更)
なお、
http://spacelike.in.coocan.jp/flat/

は、Flash 版も含めて FlatTable のトップページになっています。このページには配置図ソフトや、その説明などへのリンクが貼られています。

SVG 版 FlatTable は、ファイル形式を SVG にしたかったのですが、サーバーの設定が上手く行かないため、やむを得ず xhtml にしています。因みに、html だとタブレットで正常動作しなかったので xhtml にしました(PCだと、その辺はお構い無しなのですが)。(追記2:この問題は解決して、現在は SVG ファイルで公開しています)

Flash 版の時は、ソースは見れないので気にする必要は無かったのですが、SVG 版ではソースが丸見えなので、恥をかくことになりそうで怖いです。特に、変数名や関数名は最悪な状態です。気が向いたら修正するかも知れません。

2012年6月11日 (月)

配置コード機能の実装 -4-

FlatTable で、ページ単独の配置コードを処理する機能は優先順位が低いので実装は後回しにするつもりでいましたが、実はそうでもないことに気付きました。Flash 版 FlatTable には、ページの単純な複製機能が有りますが、SVG+JavaScript 版ではその機能をやめて、複製と球の終点への移動を合わせた機能に変えています。単純な複製をしたい場合はページ単独の配置コードを利用するつもりでいたのでした。したがって、ページ単独の配置コードを処理する機能は後回しにするわけには行かなかったのです。

そういう訳で、ページ単独の配置コードを処理する機能を実装しました。ボタン[ Code ]のメニューで Display Page CodeEdit Page Code を選択すると、それぞれダイアログボックスが表示され、ページコードの表示とページコードの編集が出来ます。全ページの配置コードには「ILC=」が必要ですが、ページ単独の配置コードには「ILC=」は付けません。

グリッドの表示

配置図 FlatTable にグリッドの表示機能を追加しました。ボタン[ Table ]のメニューで Grid on/off を選択すると、台に格子状の線が表示されます。この実装は、グリッドの画像を SVG データとして前もって記述しておいて、その表示/非表示を切り替えるだけなので簡単に出来ました・・・と言いたいところですが、ちょっと失敗をして、そのお蔭で SVG について理解を深めることが出来ました。

FlatTable の SVG データは次のような構成になっています。

FlatTable の SVG データ
<svg id="flattable">
    <g id="table">
        ・・・
        (1)
    </g>
    (2)
    <g id="balls">
        ・・・
    </g>
    ボタン類
</svg>
タグ <g> は図形を1つのグループにまとめるものです。上記 table グループにはクッションやラシャ等の台の図形が入っています。

初めは、グリッドの SVG データを上記の (2) の位置に置いていました。グリッドは、球よりも前に記述しているので球よりも後面に表示されると思っていました。しかしこの時点では何故か、グリッドが球よりも前面に表示されるのです。

実は、上記データ中に記述されている balls は台の左下に並べておく球で、台上に配置される球は、後からダイナミックに生成したものなのです。そして、それら後から生成した球は上記の tableappendChild() しているのです。そのため、table より後の位置である (2) にグリッドが有ると、それは球よりも前面に表示されることになる訳です。

球の表面にグリッドの線が有るのは美しくないので、現在はグリッドの SVG データを (1) に変更しています。球もグリッドも同じ table の child なので、後から生成された球の方が前面に来ることになります。

ところで、仮想的マウスパッドは最前面に表示したいのだが、Z軸順序の指定が出来ないので、仮想的マウスパッドが必要になる度に appendChild() を実行し、不要になったら removeChild() を実行するようにしています。しかし、上記 (2) の位置に仮想的マウスパッドを置けば、どの球よりも前面に表示されることになります。そして、不必要な時は、非表示にすれば良いのです。

しかし、以前の記事に書いたように、経路の作図中は、ゴーストや矢印球は仮想的マウスパッドよりも前面に有った方が便利なので、現在の方式のままで行くことにします。

2012年6月 5日 (火)

配置コード機能の実装 -3-

FlatTable の配置コード機能で未実装だったものの内、配置コードの入力機能を実装しました。ボタン[ Code ]から[ Input Code ]を選択すると文字入力のダイアログボックスが出るので、そこに配置コードを入力します。ダイアログボックスで[ キャンセル ]をクリックすると、入力はキャンセルされます。

ルーチンは次のようになっています。

// 配置コード入力
function inputCode(){
    ・・・
    var code = prompt("Layout Code:", layoutCode);
    if (!code || (code == "")) return;

    layoutCode = code;

    var page = firstPage.prev;
    do {                            // (1)
        page = delPage(page);
    } while (page != firstPage);
    document.getElementById("table").removeChild(page);
    document.getElementById("pgMax").textContent = "0";
    initLayout(code);
}
layoutCode は配置コードを記憶しておく大域変数です。

(1) のループとその後のコードで、それまでのページを全て廃棄しています。せっかく生成したページなので、再利用したいところですが、考えるのが面倒になったので、このような手抜きで済ませました。もし、配置コード入力の操作が何万回も実行されたら、もしかするとメモリに悪影響が出るかも知れませんが、現実的な操作としては、配置コード入力はせいぜい数十回だろうから気にしなくても良いだろうと考えています。

ところで、配置コード機能でページ単独の処理機能は今も未実装です。この機能は現行ページに対する配置コードを処理するものですが、これは全ページの配置コードから該当するページの部分を操作することで代用できます。したがって、この機能は後回しにして、他の機能の実装へ移ることにします。

試作品ではなくて、正式バージョンの公開が出来る日ももうすぐです。

2012年6月 4日 (月)

配置コード機能の実装 -2-

FlatTable に配置コード機能の一部を実装しました。今回実装したのは、配置図を配置コードにエンコードするルーチンと初期配置図作成ルーチン(配置コードを HTTP のクエリにして FlatTable にアクセスした時に配置図を表示するルーチン)です。これらのルーチンは、抜き出して端的に説明できるようなキモとなる部分は無いのでここではやりませんが、encodeLayout()decodePage() 辺りが今回実装した部分です。配置コードの仕様を基にして、力ずくでコーディングしました(すなわち、美しくない)。

画面右上のボタン[ Code ]をクリックするとメニュー[ Generate Code ]が出ます。それをクリックするとダイアログボックスが表示され、そこに配置コードが収められています。配置コードは HTTP の形式になっているので、これをコピーしてブログなどに貼り付けて利用します。あるいは、ブラウザのアドレスバーに貼り付けて配置図を直接に表示させることも出来ます。本当は、ダイアログボックスは表示させずに直接クリップボードにコピーしたかったのですが、方法が分からなかったので諦めました。インターネットで調べて、それらしいものは見つけたのですが、よく解りませんでした。

配置コード機能に関しては、まだ

  • 配置コードの入力
  • ページ単独の処理(生成、表示など)
が残っています。また、今回実装分についてもテストは不充分なので、色々なケースのテストと併せて残りのルーチンの実装をして行くことにします。

2012年6月 2日 (土)

タッチパネルはよく解らん -5-

タブレットで FlatTable の動作確認をしたら、ドラッグの応答の鈍いことが判った訳ですが、その対策として図のように、画面の両側に新たにボタンを設けて、これを押すことで球を移動させるようにしました。

配置図

まず、旨く行かなかったコードを示します。

// ボタン操作で球を移動させる
function moveByBtn(evt, delt, dir){
    var target = evt.target ? evt.target : event.srcElement;
    target.ontouchend = function (){    // (1)
        target.ontouchend = null;       //
    }                                   //
    switch (dir){
        case "x": curBall.x += delt; break;
        case "y": curBall.y += delt; break;
        default: break;
    }
    ballMove(curBall, tableAngle);

    if (target.ontouchend != null){    // (2)
        btnDelt = delt;
        willStop(target, setInterval("willGo('" + dir + "')", 200));
    }
}

function willGo(dir){
    移動ルーチン
}

function willStop(){
    ・・・
    target.ontouchend = function (){
        clearInterval(id);
        target.ontouchend = null;
    };
    ・・・
}
moveByBtn() はボタンのイベント mousedowntouchstart で起動されます。

PCはこれで正常に動作するのですが、タブレットでは旨く行きません。タブレットの場合、ボタンにタッチしたままでは球が動かず、タップならば動くのですが、今度は止まりません。

初めは (2) の条件判定と (1) は有りませんでした。しかし、タップの時に球が止まらないので、イベント touchend の発生は setInterval() の実行より前なのだろうと予想しました。それだと、willStop() を実行してもイベントtouchend を捕まえることが出来ず、球は動き続けることになります。

そこで、(1) および (2) を追加して上記のコードになりました。これならば、setInterval() より前にイベント touchend が発生しても球は停止するはずです。しかし、症状は改善されませんでした(微妙に動作が変わったような気はするのですが、よく判りません)。

そもそも、タッチしたままだと球が動かないというところから異常ですが、タブレットはそういうアホな仕様なのだろうと諦めて、とにかく動くコードに修正しなければなりません。そこで、タブレットでは、イベント touchend で移動を終了するのではなく、移動の開始も終了も touchstart で制御することにしました。すなわち、タブレットでは、ボタンをタップして移動を開始し、再度ボタンをタップして移動を終了するわけです。結局、イベントハンドラーは次のようになりました。上記コードに有った willStop() はこのハンドラーに取り込んだので削除しました。

// これで OK
function moveByBtn(evt, delt, dir){
    var target = evt.target ? evt.target : event.srcElement;
    switch (dir){
        case "x": curBall.x += delt; break;
        case "y": curBall.y += delt; break;
        default: break;
    }
    ballMove(curBall, tableAngle);

    btnDelt = delt;
    if (fTouchPnl){
        if (tpStop = !tpStop){
            clearInterval(tpId);
        } else {
            tpId = setInterval("willGo('" + dir + "')", 300);
        }
    } else {
        var id = setInterval("willGo('" + dir + "')", 200);
        target.onmouseup = function (){
            clearInterval(id);
            target.onmouseup = null;
        };
    }
}

タブレットとPCでは移動のインターバルが異なっています。タブレットでは移動のオーバーシュートが無視できないのでインターバルを長めにしました。

--------
ここまでの動作およびコードは、本ブログのサイドバー「Link」の「SVG+JavaScript 版 FlatTable の試作品」で確認できます。

« 2012年5月 | トップページ | 2012年7月 »