2014年6月3日火曜日

点の回転から複素数_Complexの計算速度を検証してみる

C言語の話題です。
はじめに
C言語では、複素数(_Complex)が仕様に加わりました。C99の拡張です。
それによって複素数が計算に使えるようになり、プログラミングの幅が広がった…
はずなんですが…

「…それ何に使うんだ?」

というのが正直な感想です。
複素数を日常生活で使うことはまずありません。
プログラムの世界でも、本来複素数を使う式は複素数を使わない方法で書き直されています。コンパイラではdouble型(実数)の高速化や、SIMDを使った並列処理が自動で行われるようになり、floatよりもdoubleの方が早いと言われる時代です。

もし複素数の計算速度が遅ければ、それを利用する必要はあまりありません。

具体的な方法
複素数を利用するもっとも身近な式として、2次元の点の回転を考えます。
点Aが与えられ、それをangleだけ回転させます。
angleの回転のx成分はMx(Mx = cos(angle);)
angleの回転のy成分はMy(My = sin(angle);)
Rx,Ryが求めたい座標だとします。
点を回転させる3つの方法を比較します。

1、複素数の乗算

ガウス平面において、乗算は点の回転を意味しますので、
p = Ax + Ay * i;
q = Mx + My * i;
result = p * q;
Rx = resultの実部;
Ry = resultの虚部;

2、加法定理

cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b);
sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b);
角度同士であればこの公式の通りですが、cosはx座標、sinはy座標だと考えると、次のように書き直すことができます。
Rx = Ax * Mx - Ay * My;
Ry = Ay * Mx + Ax * My;

3、アフィン変換

加法定理を行列の形に整理すると一次変換になりますが、アフィン変換では拡大縮小などのほか、平行移動を加えて3×3の行列に拡張されます。加法定理であげた式よりも冗長な計算が必要になりますが、プログラムでは良く利用されるため、随所で高速化されています。
今回はCoreGraphicsライブラリを利用しますので、
CGAffineTransform transform = CGAffineTransformRotation(CGAffineTransformIdentity, angle);
R = CGPointApplyAffineTransform(A, transform);
で計算します。

予想

仮説1:コンパイラの高速化が全ての方法で、同じようにかかる

だとすれば、加法定理が最も早く、次に複素数、最も低速なのがアフィンだと考えられます。
複素数の内部的な計算が加法定理と同じであれば、簡潔な加法定理が早く済みます。
アフィンは計算回数が多いため、最も低速です。

仮説2:GPUがアフィン変換を計算する

だとすれば、アフィン変換が最も早く、次に加法定理、最も低速なのが複素数だと考えられます。
アフィン変換はアニメーションの中核になっています。
高速なGPUが計算を行い、徹底的に最適化されている可能性は十分にあります。

仮説3:複素数が専用の方法で計算されている

だとすれば、複素数が最も早く、次に加法定理、最も低速なのがアフィンだと考えられます。

やってみた
読み出しと代入、三角関数の実行は計測時間に含めない。
「必要なデータが与えられてから、回転後の数値が確定するまで」を1回とし、
10000回の計算にかかる時間を計測する。
検証のためのコードは次の通りです。
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        CGPoint point;
        point.x = 3.2f;
        point.y = 4.f;
        
        const int count = 10000;
        
        CGPoint xy_result;
        
        NSTimeInterval transform_end, mult_end, xy_end, simd_end;
        
        for (int ang = 0; ang < 36; ang++)
        {
            CGFloat angle = 10.f * ang - 180.f;
            angle = (angle / 180.f) * M_PI;
            
            {
                //アフィン変換
                
                CGAffineTransform transform;
                
                transform = CGAffineTransformRotate(CGAffineTransformIdentity, angle);
                
                NSDate *begin = [NSDate date];
                
                for (int i = 0; i < count; i++) {
                    
                    xy_result = CGPointApplyAffineTransform(point, transform);
                }
                
                transform_end = [[NSDate date] timeIntervalSinceDate:begin];
            }
            
            {
                //複素数のかけ算
                
                _Complex double value, mul_value, comp_result;
                comp_result = 0;
                
                value = point.x + (I * point.y);
                mul_value = cos(angle) + (I * sin(angle));
                
                NSDate *begin = [NSDate date];
                
                for (int i = 0; i < count; i++) {
                    
                    comp_result = value * mul_value;
                }
                
                xy_result.x = creal(comp_result);
                xy_result.y = cimag(comp_result);
                
                mult_end = [[NSDate date] timeIntervalSinceDate:begin];
            }
            
            {
                //加法定理
                
                CGPoint transform_xy;
                transform_xy.x = cos(angle);
                transform_xy.y = sin(angle);
                
                NSDate *begin = [NSDate date];
                
                for (int i = 0; i < count; i++) {
                    
                    xy_result.x = point.x * transform_xy.x - point.y * transform_xy.y;
                    xy_result.y = point.y * transform_xy.x + point.x * transform_xy.y;
                }
                
                xy_end = [[NSDate date] timeIntervalSinceDate:begin];
            }
            
            {
                //加法定理をSIMDにする
                //floatのため、計算精度は落ちる
                
                CGPoint transform_xy;
                transform_xy.x = cosf(angle);
                transform_xy.y = sinf(angle);
                
                __m128 simd_point, simd_transform;
                float temp[4];
                
                simd_point = _mm_set_ps((float)point.x, (float)point.y, (float)point.y, (float)point.x);
                simd_transform = _mm_set_ps((float)transform_xy.x, (float)transform_xy.y, (float)transform_xy.x, (float)transform_xy.y);
                
                NSDate *begin = [NSDate date];
                
                for (int i = 0; i < count; i++) {
                    
                    _mm_stream_ps(temp, _mm_mul_ps(simd_point, simd_transform));

                    xy_result.x = (CGFloat)(temp[3] - temp[2]);
                    xy_result.y = (CGFloat)(temp[1] + temp[0]);
                }
                
                simd_end = [[NSDate date] timeIntervalSinceDate:begin];
            }
            
            printf("%.0f,%f,%f,%f,%f\n", angle, transform_end, mult_end, xy_end, simd_end);
        }
    }
    return 0;
}

実行結果
角度アフィン変換複素数の乗算加法定理SIMDを使った加法定理
-1800.0001610.0000340.0000460.000066
-1700.0001550.0000340.0000490.000068
-1600.0001550.0000340.0000530.000067
-1500.0001580.0000340.0000470.000068
-1400.0001570.0000340.0000470.000067
-1300.0001570.0000340.0000460.000066
-1200.0001580.0000340.0000470.000068
-1100.0001590.0000340.0000460.000068
-1000.0001590.0000340.0000470.000069
-900.0001590.0000340.0000470.000068
-800.0001590.0000340.0000460.000067
-700.0001600.0000340.0000470.000069
-600.0001600.0000330.0000460.000069
-500.0001600.0000340.0000460.000068
-400.0001600.0000330.0000460.000069
-300.0001570.0000330.0000460.000070
-200.0001690.0000340.0000470.000069
-100.0001600.0000340.0000460.000069
00.0001590.0000340.0000460.000068
100.0001600.0000340.0000460.000068
200.0001590.0000340.0000470.000067
300.0001580.0000340.0000470.000068
400.0001590.0000610.0001250.000067
500.0001650.0000340.0000460.000068
600.0001630.0000340.0000470.000068
700.0001630.0000340.0000460.000068
800.0001640.0000330.0000460.000068
900.0001630.0000340.0000460.000092
1000.0001660.0000340.0000470.000068
1100.0002510.0000340.0000420.000066
1200.0001660.0000340.0000430.000068
1300.0001640.0000340.0000470.000068
1400.0001630.0000340.0000470.000069
1500.0001640.0000340.0000470.000068
1600.0001630.0000330.0000460.000068
1700.0001640.0000330.0000460.000068
やるじゃないか複素数
目に見えて早いです。
SIMDが遅いのは、キャストに時間がかかっていることと、floatであること、式がSIMDに向いていないことが原因だと思います。コンパイラの最適化が強力なため、SIMDの利点が活かされていないようです。
しかし、数値の代入や三角関数の呼び出しもforループの中に含めると、次のようになります。
角度アフィン変換複素数の乗算加法定理SIMDを使った加法定理
-1800.0008860.0006970.0005790.000546
-1700.0009560.0006890.0005850.000539
-1600.0008930.0007520.0005620.000539
-1500.0010620.0007040.0005740.000613
-1400.0009520.0007130.0005920.000534
-1300.0008720.0006930.0005790.000532
-1200.0008650.0006740.0005740.000567
-1100.0008780.0006870.0005700.000558
-1000.0008580.0008860.0005740.000546
-900.0009480.0007130.0006140.000534
-800.0008720.0006930.0005790.000532
-700.0008800.0007120.0005860.000532
-600.0009010.0006840.0005660.000538
-500.0008610.0006740.0005740.000538
-400.0008790.0007120.0005870.000532
-300.0008780.0006930.0006220.000535
-200.0009770.0007120.0005860.000534
-100.0008770.0006850.0005660.000538
00.0004900.0001750.0001130.000263
100.0012210.0007390.0005690.000653
200.0008810.0008280.0006050.000535
300.0008760.0007120.0006760.000535
400.0008670.0006930.0005790.000534
500.0008570.0006780.0005740.000538
600.0008920.0006870.0005620.000538
700.0008950.0006940.0005800.000535
800.0008850.0007130.0005870.000532
900.0008750.0006930.0008330.000678
1000.0008720.0007310.0010710.000539
1100.0008980.0006870.0005670.000552
1200.0009280.0006830.0005790.000544
1300.0010240.0007150.0005870.000535
1400.0008690.0006930.0005790.000532
1500.0008790.0007120.0005860.000531
1600.0008920.0006970.0005600.000539
1700.0008790.0006750.0005740.000539
複素数は明らかに遅くなります。先の表でも見た通り計算自体は遅くないのですが、creal、cimagなど、データを読み出す部分がインライン定義されていないため、ボトルネックになります。

考察
複素数には専用の計算方法が使われているらしい。
回転を繰り返す場面であれば複素数による高速化も可能性があるのではないかと思うのですが、そのほかの場面では、doubleを使う方が早いのだと考えるべきだと思います。

2014年3月25日火曜日

EightReportsの裏技

EightReportsについて、分かっている限りの裏技を書とめておきます。裏技というより、「故意に設置してあるものの実装されていることが分かりにくいもの」でしょうか。

ver 3.0で使える裏技

・一部のトレジャーは復活する
サブダンジョンの「虚ろなる同心円」だけは、ダンジョンの出入りをするたびにトレジャーが復活します。
トレジャーの中身はランダムで、下層に行くほどはずれの確率が増えていきますが、下層に行くほど得られるアクセサリが強力になります。
最終層では、シーネ、フォルト双方の最強武器と鞄が出ます。
・放置していると中身が変わるトレジャーがある
王女と出会う終盤まで取らないでいると、一部のトレジャーは中身が変わります。
・シーネの銃がジャムる
複数の敵があまりにも近くにいるとき、まれに弾詰まりを起こして銃撃に失敗することがあります。
・スキルの超複合
スコール判定中に一定のスキルを使うと、さらに補正量が増え、状態の名前が変わります。ただ、LPとCPが足りなくなるため、よほど余裕のある戦闘でない限りは起こらないものも含まれます。
状態の名前 補正量 条件
スコール 2~3倍 スコールを使用する
ダイアモンドダスト 2~4倍 スコール中にペルティエを使用する
ヒートスチーム 2~4倍 スコール中にテルミットを使用する
ホワイトアウト 2~5倍 スコール中にペルティエとテルミットを使用する

没になった実装

アップデートによって増えたシステムが多いのですが、アップデートによって廃止されたシステムもあります。
・スキル攻撃の巻き込み
知力が影響値よりも低いとき、スキルの範囲攻撃で味方を巻き込んでしまうシステムがありました。
ディラックだと100%起こってしまうため、「誰もディラックを使わなくなる」ということで廃止しました。

2014年3月8日土曜日

LaTeXの四則演算を考える

プログラム言語として見たLaTeXについて書いていきます。
長くなるので、今回は計算だけに内容を絞ります。

1、数値を計算する
TeXとC言語の計算の書き方を比べていきます。
緑がC言語流、青字がTeX流です。
%変数の定義
int a, b, c;

\newcount\vA
\newcount\vB
\newcount\vC
\newcount\vD

vC = f(vA, vB)
を考える。


%足し算
vC = vA + vB;

\vC = \vA
\advance\vC by \vB


%引き算
vC = vA - vB;

\vC = \vA
\advance\vC by -\vB


%かけ算
vC = vA * vB;

\vC = \vA
\multiply\vC by \vB


%整数の割り算
vC = vA / vB;

\vC = \vA
\divide\vC by \vB


%剰余算(割ったあまりを求める)
vC = vA % vB;

\vC = \vA
\vD = \vA
\divide\vD by \vB
\multiply\vD by \vB
\advance\vC by -\vD


%小数の割り算(TeXでは固定小数点小数を使うしかない)

float fa, fb, fc;
fa = 10.f, fb = 3.f;
fc = fa / fb;

fcには3.333333が入る(有効桁数が6のとき)。

\vA = 10
\vB = 3
\vC = \vA
\multiply\vC by 1000
\divide\vC by \vB
vCには3333が入っているので、これを3333/1000とみなす。
上で見た通り、(後発のスタイルファイルがあるかもしれませんが、それを除くとすれば)TeXにはいくつかのものがありません。
  1. 非負整数や浮動小数点小数が無い。
  2. 剰余算が無い。
  3. returnを持たないため、関数は値を返さない。
2、スコープの違い
また、TeXにはローカル変数やスコープがないため、下のような違いがあります。

int out_of_scope;
{
____int value;
____value = 10;
____out_of_scope = 10;
}

printf("%d", out_of_scope); //書ける
printf("%d", value); //コンパイルが通らない
//out_of_scopeにはアクセスできるが、valueにはアクセスできない。
//{}を過ぎると変数は破棄される。



\newcount\outofscope
{
____\newcount\value
____\value = 10
____\outofscope = 10
}

\the\outofscope %10が出る。
\the\value %同じく10が出る。
%どちらでもアクセスできる。
%{}を書いてもスコープの意味はないため、定義した変数はプログラムの終了まで保持される。
TeX流も便利そうだと思うかもしれませんが、これでは困ることが出てきます。

int itl, limit;
itl = 0;
limit = 300;
while (itl < limit) {

____int value;
____value = 0;
____itl++;
}

//C言語であれば、こんなコードでプログラムが続行不能になることはありません。
//変数valueが定義されますが、ループを一回繰り返すたびにvalueが破棄されているためです。



\newcount\itl
\newcount\limit
\itl = 0
\limit = 300

\loop
____\newcount\value
____\value = 0
____\advance\itl by 1
\ifnum\itl<\limit \repeat

%同じ意味のコードですが、TeXでは実行できません。
%ループを一回繰り返すたびにvalueが確保されています。
%同じ名前でも新しく確保されるため、結果として300個のvalue変数を確保しようとします。
%LaTeXでは256個を超える変数カウンタは確保できませんので、ループ中にハングします。
関数の中で\newcountを使ってしまうと、ループが原因でハングすることがあります。
そのため、グローバル変数にローカル変数らしい振る舞いをさせることで対応します。
ざっくりと以上です。それでは、また。

2014年3月7日金曜日

LaTeXで法律文書を作る

今回は法律文書を作ります。
実用というよりもコード中心の話になるので、写真は置きません。
ソースコードはここからダウンロードしてください。
emagital.com/Blogs/legal.zip
この記事で説明することは2つです。
  1. 数値をイロハや丸付き数字に変更する方法。
  2. LaTeXでキー値コーディングを扱う方法。

1、数値をイロハや丸付き数字に変更する
イロハの出力は、Switch文でできます。
\newcount\tempvls
\def\toIroha#1{
____\tempvls=#1
____\ifcase\tempvls
________\or\or\or\or\or\or\or\or\or
________\else 未定義
____\fi
}
\toIroha{数値}を呼び出すことで変換されます。
この関数を書き換えることでたいていの変換ができますが、丸付き文字についてはフォント自体の問題が出てくるため、そのまま入力しても出力できないことがあります。
そこでpictureを使って、circleと数字を重ねます。
数値は適宜調整してください。
\def\marutsuki#1{
____\unitlength=1zw
____\begin{picture}(1,1)(0,0)
________\put(0.82,0.42){\circle{1.3}}
________\put(0,0){#1}
____\end{picture}
}

2、LaTeXでキー値コーディングを扱う
たとえば
居住用財産の買換えの場合等の長期譲渡所得の課税の特例
で触れている内容について書きたいことがあるとします。とても長い条目です。
「租税特別措置法の居住用財産の買換えの場合等の長期譲渡所得の課税の特例に上げた通り」なんて愚直に書こうものなら、それだけで紙面を潰してしまいますから、「第七条に上げた通り」などと書き換える必要があります。
しかしそのまま「第七条」と書いてしまうと、その前に新しい条を挿入して、番号が変わってしまったときに苦労します。番号が変わるたびに、第何条だ第何款だ第何項だと書いていた番号を片端から書き換えていくのは骨が折れます。そんな面倒なことはしたくない。
だからこういう方法を考えます。
  • key:居住用財産の買換えの場合等の長期譲渡所得の課税の特例
  • value:第七条
とするときに、キーである「居住用財産の買換えの場合等の長期譲渡所得の課税の特例」と打ち込むと、自動で「第七条」に変換されます。
キーと値を対応させるため、キー値コーディング(key value)と呼ばれる方法です。
LaTeXでは、それをラベルの再定義によって実装できます。
なお、値とキーのひも付けは一回前のコンパイルの時に行われています。
ですので、二回コンパイルするまでは反映されません。データがない間は??で置き換えられます。
%条番号を定義
\newcount\regsect

\makeatletter
____%labelと同様のものを別名で定義
____\newcommand{\slabel}[1]{%
____\@bsphack
________ \protected@write\@auxout{}%
________________{\string\newlabel{#1}{{\the\regsect}{\the\page}}}%
____\@esphack
____}

\makeatother

%呼び出し
\newcommand{\refSection}[1]{第 \ref{#1} 条}
\slabel{居住用財産の買換えの場合等の長期譲渡所得の課税の特例}と書くことで、第何条かという情報が登録されます。
その時点での\regsectの値と、「居住用財産の買換えの場合等の長期譲渡所得の課税の特例」がひもづけられます。
\refSection{居住用財産の買換えの場合等の長期譲渡所得の課税の特例}と書くと、ひもづけられた\regsectの値と置き換えられます。
  1. 居住用財産の買換えの場合等の長期譲渡所得の課税の特例は\refSection{居住用財産の買換えの場合等の長期譲渡所得の課税の特例}です
  2. 居住用財産の買換えの場合等の長期譲渡所得の課税の特例は第七条です
上の文章を入力すると、下の文章が出力されます。
詳細については実際に使っている部分を参考にしてください。legal.zipの中の、legal.texにある最終行がその部分です。
ざっくりと以上です。それでは、また。

2014年3月6日木曜日

EightReports ver3.0を公開しました

まとまった文章を書くスペースが必要になったのと、LaTeXまわりの技術的な話をまとめようかなとかねがね思っていたので、ブログを始めることにしました。
と、挨拶はここまでとして宣伝でございます。

なにはともあれ、iOSゲーム『EightReports』のアップデート(Ver3.0)が出ました。
また完結記念として、三月末までの期間限定でEightReportsを300円→100円にします。
EightReports - Shota Oki
無料版の『トゥオネラの魔』も更新しました。
トゥオネラの魔 - Shota Oki
シナリオは完結し、完全な形で英語翻訳もされました。
バグフィックスをして、システムの不都合な部分を修正しました。
敵キャラクタも増えましたし、フィールドも増えました。
これで拡張はおしまいです。
今後、バグフィックス以外の理由で修正を入れることはありません。
とてもいいゲームになったなと思います。

1、シナリオの話
シナリオの原型を書いたのが2010年、ゲーム用にまとめ直したのが2011年です。
その2011年の初稿と実際のゲームを比べると、かなり印象が違います。
見比べるのも一興かと思うので、初稿のあらすじを載せておきます。
(比較対象のゲーム本編のあらすじは、ゲーム本編で見てください)
戦争(=公共事業)に依存しすぎた国があった。
終戦を迎えたが、十分な賠償金が得られず国債は返済できなかった、労働可能な若者も残っていない、農地は荒れ、学校の閉鎖を続けたことで識字能力のない子供が増え職人としての養成ができない、他国との競争力もない。結果として経済が支えられなくなり、せめて軍用品の買い上げと兵隊の雇用を続けるために終戦を隠すことにした。
それを調べるのが連合から派遣された女性外交官(シーネ)であり、シーネには王室警護の男(フォルト)が護衛としてつけられた。
賠償金を得られなかった理由は、強力すぎる兵器を使ったことで相手の国としての機能そのものが失われたためである。教授の作り上げたそれは、アイダベルと呼ばれていた。(本編で使う場所が多いので掲載しない)
女がいた。女はフォルトが護衛として人を殺す姿とその技術に惹かれていて、自分自身がその美しいものの一部になること(=殺される対象になること)を望んでいた。フォルトに自分が危険な人物だと思わせようとしていたが、何をしてもフォルトの気を引くことができない。彼女は隠棲していた王女の首を切り取ってフォルトに手渡すことにした。激怒したフォルトは女の望み通りに首の骨を折って殺すが、それを見ていたシーネはフォルトから距離を置くようになる。
フォルトは国王をかばうためにシーネを裏切り、寝返った。
エンディングでは、シーネが「事実は正確に報告するように」と釘を刺されるが、フォルトを手にかけたことが後ろめたい彼女は「報告書は嫌いよ(I hate reports.)」と小声で応じる。それを聞き違えたジャーナリストが、Eight Reports(八部の報告書)があるのだと受け取って、単身で取材を始める。

こういう話になる予定だったんですね。
予定は予定であって、ならなかった。
なぜならなかったのか、というと予算と開発期間の都合です。
このゲームの開発費は飛び抜けて安い部類に入るはずです。
プログラムを組むのは僕一人です。ライブラリや開発環境の多くを自作して、古いMacBookProで開発できるようにしてありますから、機材費もそうかかりません。ライセンス料やロイヤリティを払う必要はなく、リソースはプログラムによる使い回しや代替で少ない。
それでも開発にかかる実費がゼロにはなることはありません。
実費を削ればそれだけ時間がかかります。
このくらいは開発ができるだろう、という見通しの立った時点で実現可能な部分に絞り込んで、そこにフォーカスを当てて完結させる必要がありました。もちろん完全にシェイプを絞られた話ではありませんし、初稿の頃の名残がありますから、キャラクタの配置や伏線を見た時に「このキャラは必要なの?」と思う部分があるかと思います。それを含めての紆余曲折というか、インディーズらしさを楽しんでいただけたらなと思います。

2、翻訳の話
それとシナリオの完結に合わせて、英語翻訳されています。
とはいっても僕はさっぱり英語ができない。
じゃあ業者に発注にかけようか、というとそれも難しい。NPCとの会話、ゲーム中のアイテムの説明文、イベントのセリフがありますから、量自体が相当なものです。それに肝心の内容は翻訳しづらいことこの上ない。
たとえばアイテムに「輓近代数学の展望」(太平洋戦争中に出版され、当時の最先端の数学がまとめてありましたから、学徒出陣した学生が戦地に持っていったと言われています)が出てきますが、そのタイトルを翻訳したところで、英語圏のプレイヤーにとっては(日本での意味合いほど)強い意味やストーリー性のあるものではない。代替の翻訳を当てることになるでしょうが、業者相手でそういう擦り合わせは難しいんじゃなかろうか。そういう理由で翻訳はほとんど諦めていました。
StudioLoupeのリオ・リーバス先生が専業開発を引退されるとお聞きしましたので、(気が引ける量ではありますが…)英語翻訳をお願いしてみましたところ、翻訳を快諾していただきました。難しいかと思っていた部分もスムーズに進みまして、素晴らしい翻訳になりました。
原文:「靴墨とシャンパン」
翻訳:A shoe polish and a champagne,
原文:「詩学 第二部」
翻訳:Poetics – The Lost 2nd Book
かなり丁寧な翻訳だと実感した二文をあげてみました。
対訳の確認をしている時に個人的に感動した二文です。
靴墨の英語には二種類あり、乳化性か油性かで名前が違います。なぜ日本ではシューパフが靴墨と同じ名前で売られているの、という話に出てくるあれは乳化性の靴墨です。だから僕も最初に見た時は「shoe creamじゃないの?」と思いました(頼んであってよかった…)。シャンパンやウィスキーで靴墨を溶いて磨くときには、油性の靴墨を使いますから、shoe polishになります。
また、ウンベルト・エーコが「薔薇の名前」で話題にした通り、アリストテレスの書いた詩学の第二部は特別な本です。その存在が第一部の巻頭で宣言されていますが、実物は見つかっていません。それが良くわかる翻訳じゃないかと思います。
原文:
「人間はただの数かね?」
「数は文句を言わんし、失敗もせん」
翻訳:
So they're just numbers to you?
Numbers don't complain, and don't let me down.
これも素晴らしい翻訳です。
「人間を数でしか見ないなんてきみは実にひどいやつだ」
「へまをせず文句も言わないだけ、無能より数字の方がマシさ」
と、原文を丁寧に書くとこんな感じのやり取りなんですが、ぼかさずに書くと野暮で気障になりますし、あまりにも長くなります。原文の言い方を直訳すればand they don’t fail.となるはずですが、直訳では原文の意図から外れます。「数が私を落胆させることはない」のニュアンスはかなり近い。
こんな具合に、原文のニュアンスを綺麗にまとめてくださった翻訳を頂いております。
もし機会がありましたら、iOSの言語設定を日本語以外にして、英語版でも楽しんでいただければと思います。
3、それ以外の話
アップデートでグラフィックと音源の入れ替え、アドバタイズの充実、操作方法の拡張を行いました。
アドバタイズとして、これまでは説明書を使っていました。説明書を作って、これで操作方法は分かるだろうと踏んでいたんですが、読むのになんというか手間がかかってしまって、自分で見ていても読みそうにない。これくらいならゲーム中で説明が流れた方がいい、ということでゲーム中にアドバタイズが出るようにしました。特定の操作をした時点で画面に説明文が出てきます。
操作方法も少し増えました。フリック操作に対応したことで、iPhoneの操作に近い感覚で動かせるようになりました。デバッグでもずいぶん楽になったことを実感しました。操作感のストレスが軽減されただけでなく、音とグラフィックの調整も入りましたから、かなり気持ちのいい戦闘になりました。
どうぞよろしくお願いします。
ここまでにします。それでは、また。

2014年3月5日水曜日

LaTeXではがきの宛名を作る

MacやLinuxを使っていて困ることに、日本独特の文章があります。
Windowsなら用意されている便利なテンプレートがMacにはありませんから、年賀状にしても縦書きの文章を作るにしても困ることになります。
もちろんお金があればWindows機が別にあってそれを使って作ることができるでしょうし、絵を描くためにMacを使っているユーザーであればIllustratorの力技で作ることもできるでしょう。ただそれは「ちょっとした用途」のためだけで考えるにはあまりにも高価……できれば無料で、仕上がりが綺麗で、将来的にWindowsに移った後も同じデータが利用できる、というのが理想じゃないですか。
そこでLaTeXの出番です。
LaTeXとは、論文の作成を目的として開発されたマークアップ言語(ラッパであるLaTeXがマークアップ、TeX自体は関数型言語だとも言われます)のことであり、日本ではアスキーが管理するplatexが広く使われます。Mac、Windows、Linuxの全てのプラットフォームで動作し、誰もが無料で利用することができます。

今回はそれを使ってはがきの宛名を作ります。2円切手が復活したということでなかなかのタイムリーな話だと思います。
年賀状を作る時に便利です。MacやLinuxでも動作するのが嬉しい限りです。
例の通り、テストとして打ってある名前や住所はでたらめです。

ソースコードはここからダウンロードしてください。
emagital.com/Blogs/Address.zip
この記事では、「データを入力→自動でレイアウトしてプリント」するところまで扱います。
スクリプトやプログラムを組んでやれば、csvやデータベースからプロットしてまとめて印刷することもできますが、文量が増えてしまうのでこの記事では扱いません。とりあえず余白だけは作りますが、実作業については追々やりましょう。

1、つかいかた
Address.texをコンパイルするとpdfが出力されます。
Macのプレビューで印刷する場合は、サイズ調整は「100%」、用紙サイズは「はがき」にしてください。 あとは紙の厚さや印刷設定をして印刷をするだけです。
Address.texはこういう内容になっています。
%未設定時の情報を指定します
\input{System/default.tex}
%あなたの住所を設定しているファイルを指定します
\input{Sender/Sakon.tex}
%送り先の住所を設定しているファイルを設定します
\input{AddressList/Dosetsu.tex}

%switch文です。switchの値を変えると、レイアウトが変わります
\newcount\switch
\switch=1
\ifcase\switch
%switchが0の時、レターパックの住所欄に合わせて情報を印刷します
%枠に合わせてはさみで切って、住所欄にのり付けしてください
\input{System/Formats/LetterPack.tex}
\else
%switchが1以降の時、はがきに情報を印刷します
\input{System/Formats/Hagaki.tex}
\fi

\begin{document}

%描画関数です
\draw{}
\drawsenderinfo{}
%レターパックに出力する時は、下のDrawOptionを有効にすることで、送り品の詳細を書くことができます
%\drawoption{ソフトウェア}

\end{document}
送り先とあなたの住所を書き換える時は、Sender/Sakon.tex(あなたの住所)、AddressList/Dosetsu.tex(送り先の住所)をそれぞれ書き換えてください。ファイル名を変えた場合は、Address.texのパスも変える必要があります。
%名前
\def\sendername{島 左近}
%郵便番号
\def\senderaddressnumber{456-8876}
%住所
\def\senderaddress{山梨県甲府市山梨町四の七}
%電話番号
\def\senderphonenumber{050-0000-1111}
%名前
\def\receivername{立花 道雪}
%郵便番号
\def\receiveraddressnumber{123-5432}
%住所
\def\receiveraddress{大分県大分市大板町}
%二行目の住所
\def\receiversubaddress{マンションオオイタ三○八号室}
送り先の\receiveraddress、\receiversubaddressで住所が定義できます。同様にあなたの住所は\senderaddress、\sendersubaddressで定義できます。慣例的にLaTeXは関数名を全て小文字で書くため、ぱっと見た感じでは読みにくいこともあるのですが、「sender(sub)address」であり、subがつくと二行目になります。定義の有無を判別して内部でレイアウトを変えていますので、二行に分けないと崩れる、ということはありません。
ただ、住所や名前に半角英数を使うと文字が横に倒れます。

2、なかみの話
仕組みとしては名刺とあまり変わりません。位置合わせの必要な郵便番号はpicture環境、縦書きの住所はplextで拡張されたminipage環境で描画しています。
ただ、変わったことをしている場所が一カ所だけあります。郵便番号の字間を\kanjiskipで調整してしまうと、はがきの字間が調整しづらくなってしまいますし、文字サイズを変えた時にずれが出てくるため不適当なんです。
そこで下の関数を使って、一文字ずつパースして取得しています。
\makeatletter
%配列と変数の宣言
____\newcount\@faise
____\edef\numa{0}
____\edef\numb{0}
____\edef\numc{0}
____\edef\numd{0}
____\edef\nume{0}
____\edef\numf{0}
____\edef\numg{0}

%parse:文字列を分解し、配列に1文字ずつ入れていく
\def\parse#1 {
____%変数を初期化、引数もedefで展開して関数化する。
____\edef\@input{#1}
____\@faise=0
____%文字列を分解、forで一文字ずつ取得する。
____\expandafter\@tfor\expandafter\member
____\expandafter:\expandafter=\@input\expandafter\do{%

________%n文字目に対して処理を実行する
________\ifcase\@faise
____________\edef\numa{\member}%
________\or
____________\edef\numb{\member}%
________\or
____________\edef\numc{\member}%
________\or
____________%4文字目のハイフンには何もしない
________\or
____________\edef\numd{\member}%
________\or
____________\edef\nume{\member}%
________\or
____________\edef\numf{\member}%
________\or
____________\edef\numg{\member}%
________\else
________\fi

________%文字カウントを進める
________\advance\@faise by 1
____}
}
\makeatother
ざっくりと以上です。それでは、また。

2014年3月4日火曜日

LaTeXで名刺を作る

MacやLinuxを使っていて困ることに、日本独特の文章があります。
Windowsなら用意されている便利なテンプレートがMacにはありませんから、年賀状にしても縦書きの文章を作るにしても困ることになります。
もちろんお金があればWindows機が別にあってそれを使って作ることができるでしょうし、絵を描くためにMacを使っているユーザーであればIllustratorの力技で作ることもできるでしょう。ただそれは「ちょっとした用途」のためだけで考えるにはあまりにも高価……できれば無料で、仕上がりが綺麗で、将来的にWindowsに移った後も同じデータが利用できる、というのが理想じゃないですか。
そこでLaTeXの出番です。
LaTeXとは、論文の作成を目的として開発されたマークアップ言語(ラッパであるLaTeXがマークアップ、TeX自体は関数型言語だとも言われます)のことであり、日本ではアスキーが管理するplatexが広く使われます。Mac、Windows、Linuxの全てのプラットフォームで動作し、誰もが無料で利用することができます。

今回はそれを使って名刺を作ります。
見ての通り、テストとして打ってある名前や住所はでたらめです。

ソースコードはここからダウンロードしてください。
emagital.com/Blogs/NameCard.zip
文字にホワイトシャドウを入れられないのか、だったらいいや、と思った本格派のあなた。
それは追々やりましょう。とりあえず今回は、レイアウトを名刺用に組版して、印刷できる状態にするところまでです。

1、つかいかた
Meishi.texをコンパイルするとpdfが出力されますから、それを厚手のケント紙に印刷して、断ち切り線に定規を合わせて切ってください。
ここで名刺の内容を書き換えたいのなら、ここの数字とパスを変えてください。
\newcount\switch
\switch=1
\ifcase\switch
%%0のときに実行
\input{NameList/Private/Test.tex}
\or
%%1のときに実行
\input{NameList/Private/Test.tex}
\else
%%2以降のときに実行
\input{NameList/Private/Test.tex}
\fi
Switch文です。inputの参照先のパスを変更する方法は便利です。inputされたファイルが参照され、そのファイルで定義されている関数が実行されます。
これが指しているファイルの内容を書き換えてください。
%%背景画像を指定
\def\pictureName{NameList/res/mulsimg.jpg}

%%ライン色(この場合はシアン色)を指定
\def\lineColor{cyan}

%%これを書き換える
\def\drawMeishi{
____\put(0, 0){\framebox(\the\mwidth,\the\mheight){\LARGE{螺鳥 学}}}
____\put(0, 7){\framebox(\the\mwidth,\the\mheight){\large{\textsf{LaTeX Manabu}}}}
____\put(10,5){\footnotesize
________\put(0, 7){東京都文京区大字歩外 1-1-1}
________\put(0, 3.5){Tel:080-xxxx-1234}
________\put(0,0){Mail:latex@hogehoge.com}
____}
____\put(5,44){
________\put(0, 5){\framebox(80,0){株式会社テストコード\hfill 第三業務部}}
________\put(0, 0){\framebox(80,0){\hfill 部長代理}}
____}
}
\drawMeishi関数の中にあるのはpicture環境用のコードです。
putの後で指定されているものがxyの座標、そのあとの{}でくくられた場所に文字を書くと指定した座標に文字や図形が出力されます。putは入れ子にすることで相対座標にすることができます。
画像を変えるにはNameList/res/mulsimg.jpgを書き換えてください。画像の大きさが変わると設定が面倒になるので、mulsimg.jpgをコピーして、画像編集ソフトで開いて、その上にデータをペーストすると楽です。

2、なかみの話
幅90mm高さ55mmのparboxを作って、中身の描画関数を外部ファイルで定義、それをtabularで並べています。背景として、画像とpicture環境で作った断ち切り線を描画。これらの背景は大きさのない箱として独立して描画されるため、有無によってレイアウトが変更されることはありません(背景の描画方法については、奥村さんが書いておられるこのマクロがベースです)。
ざっくりと以上です。それでは、また。