Dec 25, 2015

特定の文字列を含まないという正規表現

正規表現で、特定の文字列を含まないパターンを記述するのは難しい。

その文字列の文字数が少なければ、例えば"ABC"を含まないとするなら

([^A]|A[^B]|AB[^C])*
という方法がありそうで、筆者は時々やってしまうのだが、これは正しくない。"AABC"があると"AA"がA[^B]にマッチ、"ABABC"があると"ABA"がAB[^C]にマッチしてスルーしてしまうし、"A"や"AB"で終わるとマッチしないからである。
この方向で進めると、一例としては、
([^A]|A(B?A)*([^AB]|B[^AC]))*(A(B?A)*B?)?
という式になるそうだが(大崎 博基さんの「Perl正規表現雑技」より)、これは少なくとも筆者には読めない。

特定の文字を含まない、という式は、例えば"A"を含まないなら[^A]*と簡単に書けるのに、特定の文字列を含まない、となると途端に難しくなるのは不思議である。

特定の文字列を含まない行にマッチする、よく知られたパターンとして、

(?!.*ABC).*
というやつがある。(?!...)はnegative lookahead assertion(否定先読み)というやつで、その先に...が続かない、という条件を表す。(?=...)はその先に...が続くというpositive lookahead assertionであり、ABC(?=DEF)は"ABCDEF"の"ABC"にマッチし、ABC(?!DEF)は"ABCDEF"の一部でない"ABC"にマッチする。
(?=...)(?!...)は、POSIX仕様などの正規表現の古い仕様には含まれず、grepなど一部使えない処理系があるが(Emacsもサポートしていない。外部コマンドを活用しろということらしい)、PerlやJavaScriptでもサポートされており、現在はほとんどの処理系で使えると言える。

(?!.*ABC).*の弱点は、文字列全体のマッチにしか使えないことである。例えば、文字列中の"STA"〜"END"の部分を検索したいが、途中に"ABC"を含む場合は除外したいと思って、STA(?!.*ABC).*ENDと書くと、"STAxxENDxABC"のように、"END"の後に"ABC"がある場合もマッチしなくなってしまう。

今回、そういうのが必要になって、悩みながら、何かいい方法は無いかと思ってWeb上を探してみたら、

((?!ABC).)*
というのを見つけた。この文字から先が"ABC"でない任意の文字が0個以上、である。すごい発想だと思った。

Web上には(.(?!ABC))*と書いている例もあり、同じだと勘違いして、早速(.(?!ABC))*を使い始めたのだが、よく考えると(.(?!ABC))*には問題があることに気付いた。これだと、"ABC"を2文字目から検索することになり、先頭がABCの場合に除外されず、マッチしてしまうのである。さらに、例えば、途中に"EN"を含まない"STA"〜"END"の部分を検索するつもりでSTA(.(?!EN))*ENDと書くと、途中に"EN"が無くても、"END"の直前の1文字が必ず.(?!EN)に当てはまらない("END"の"EN"で引っ掛かる)ので、何にもマッチしなくなってしまう。
((?!ABC).)*にはこれらの問題は起こらない。
それと対称にして、(?<!)のnegative lookbehind assertionを使って(.(?<ABC))*としても上記の問題は起こらないが、より複雑だし、手元のPerlで試すと10%ほどの速度低下が見られたので、これを使うメリットは無いだろう。

結局、外側の()によって後で参照可能なグループが増えないように、(?:(?!ABC).)*という形にすることによって、筆者の問題は解決した。

See more ...

Posted at 20:55 in PC一般 | WriteBacks (0)
WriteBacks

Dec 06, 2015

スパコン「京」を目撃

昨日、神戸市他が主催する、一般向けセミナー「意外と知らない?暮らしを支えるスパコンの働き」に行ってきた。

その前の見学会で、スーパーコンピューター「京」を見てきた。

下の階でポスターの展示をゆっくり見てたら、「京」を見る時間が10分くらいしか無くなってしまった。
見学室に入ると、説明員の方が会場からの質問を受けていた。次々に質問がなされていて、
「京」は一般向けに時間貸ししていること(部分的に借りれる)、
「京」の100%を借りると一時間百数十万円であること、
「京」を100%使う用途も20以上あるが他の人が使えなくなるので月1〜2日に決めていること、
CPUはSPARCというやつ、
OSがLinuxであること、
「京」の候補地は最後に神戸と仙台に絞られ、インフラの利で神戸に決まったこと、
2020年運用開始予定のポスト「京」も今と同じ場所に作られること、
がわかった。

セミナーの開会の挨拶では、文部科学省の人がスパコン研究プロジェクトの必要性を熱く語っておられた。
「行政事業レビュー」で「数ある事業の中で最も説明されてこなかったのがスパコン事業だ」と厳しく追及された件は、実際には色々説明していたが、政治家に発せられたその一言だけをマスコミが取り上げてフォローしないから世間的にスパコン研究が無駄という印象になっているのが実態であること、なぜ「2位じゃダメなんでしょうか」の理由は、単純明快なので何度も言われてきたことだが、1位のものを製造する技術があって初めて国際的な競争力になるから、ときっちり説明された。

講演は4つなされた。セミナーは撮影禁止だったので、細かい内容は伏せるが、無難な概要と、筆者がした質問と講演者からの回答を記しておく。

1. 「シミュレーション事始め」
 シミュレーションするには、物理モデルを作って、四則演算のみのアルゴリズムにして、プログラムを作る、精度を上げるには解像度を上げる(シミュレーションする三次元的な位置を細かく取る)必要があり、3乗や4乗(三次元+時間)のオーダーで計算量が増えるのでスパコンが必要になる、スパコンの根幹の技術は複数台のコンピューターを接続する技術、という一般的な話。
Q:インターネット上の複数台のコンピューターを接続して並列計算を行う場合の接続技術とスパコン内のプロセッサーの接続技術との特徴的な違いは?
A:距離と遅延。隣のノードの計算結果が頻繁に必要になる場合はスパコンが有利。

2. 「『京』が拓く天気予報の未来」
 スパコンによるシミュレーションと、全方位レーダーによる高速で詳細な雨雲の観測などの最新の観測技術を組み合わせることによって、雨雲が無い状態から30分後に発生するゲリラ豪雨も10分や15分前に予測できるようになるなど、新たなことができるようになる可能性が色々あるという話。

3. 「世界最先端スパコンが変えるタイヤづくり」
 タイヤの分子構造を兵庫県にあるSPring-8で計測して「京」てシミュレーションすることによって、低転がり抵抗、高グリップ性能のタイヤの耐摩耗性能を倍にする方法を発見したという話。
Q:動的な物理モデルの正確さはどのようにして確認したのか、正確でなかった時はどうやって改良したのか?
A:やはり高性能な計測機器を用いて実物の動きを計測しながらモデルを作成している。

4. 「電気自動車高性能化への挑戦:スパコンと放射光が解き明かす電池の姿」
 SPring-8と「京」を用いることによって原子構造、電子構造のシミュレーションを実現し、充電式電池の性能を向上する構造を見つることができたという話。

See more ...

Posted at 12:43 in 雑記 | WriteBacks (0)
WriteBacks

Aug 30, 2015

10キーで棋譜入力するiモードアプリ

筆者は未だにdocomoのガラケーを使っている。会社でも家でもネットに繋がったPCが手元にあるし、電車通勤ではないし、遠出も頻繁にはしないので、スマホを使う時間が無いのである。外出先では携帯電話は電話とメールとカメラさえ使えれば良く、音楽はiPodで聞くので、日常生活でスマホの必要性を感じる瞬間が全く無い。今だと携帯電話のランニングコストが年間2万円以下で済んでいるので、端末費合わせて2年で20万円かかるようなスマホを購入する気にはならない。

というより、今使ってるP-01Cを大変気に入っているのである。シンプルなデザインで軽くて薄くて自作アプリが動く。完璧である。

筆者はこのiモード携帯に自作の棋譜再生アプリをインストールして活用しており、筆者の1日数分間の将棋ライフに欠かせないものとなっている。

日々、数字ボタンと*#だけで将棋盤を操作していて、ある日、ふと、「7六歩」といった符号を761とかで入力できたら、意外と高速に棋譜入力できて、出先で棋譜を記録できて便利だったりしないか、と思った。筆者は時々、反省のために対局後に盤面を撮影するのだが、記憶力が無く、家に帰って写真を見ると既にそこまでの手順が再現できないことがよくあり、できるなら棋譜を記録したいのである。

携帯電話のアプリでよくある、カーソルキーで駒を選択して動かすUIは、ボタンを押す回数が尋常ではなく、あまりにも手間で時間がかかる。
パソコンやスマホであれば、棋譜入力はマウス操作やタッチパネル操作で行うのが通常で、これは相当に楽だし高速であるが、1手当たり3キーで入力できれば、それよりも速いのではなかろうか。
棋譜の符号に慣れている人でないと使入力しにくいだろうが(チェス初級者の筆者はコマンドライン版gnuchessのNf3とかBxf6といった符号入力に最後まで慣れなかった)、記録した棋譜は符号で書かれているのが一番読みやすいと思うので、そういう棋譜を求めるなら、記録される通りに符号で入力できるのが最善だと思う。

棋譜の符号の数字部分は2桁であり、数字ボタン2発で打てる。数字は1-9なので、0ボタンを「同」にすれば、到達地点の部分は全て2ストローク以内で入力できる。
その次の駒の種類(以下、3キー目と記す。実際には「同」なら2キー目である。)は、成駒を含めると14種類あるので、数字ボタン1回で入力可能とは限らないが、9種類以上の駒が1つのマス目に行ける(または打てる)ことは稀であるので、大抵数字ボタン1回で入力できる。
それ以上に入力が必要な場合というのは、「成」と「不成」がある場合と、同じ種類のそこに行ける(または打てる)駒が2つ以上あり、「打」「左」「引」とかで特定しないといけない場合である。飛車は2枚しかないので、例えば左右で特定するとして、最大でも「左成」「左不成」「右成」「右不成」の4通りである。角も2枚しか無いので同様、桂も最大2ヶ所からしか来ないので同様に最大4通りである。金の動きをする駒は成れないので、最大で「と」の場合に6つの駒を特定する為の6通りである。銀は最大4枚で全て盤上にあってそれぞれ「成」「不成」があれば8通りである。香歩は特定が必要になることがないので、「成」「不成」の2通りである。
従って、駒の種類より後の部分は最大8通りであり、8つのボタンに動的に割り当てれば、数字ボタン1回で入力できる。(以下、4キー目と記す。)

さらに、3キー目と4キー目の組み合わせが10種類を超えることは稀だから、それらをまとめて、例えば「玉」と「金左」と「金右」の選択肢が一緒に出るようにする方がボタンを押す回数は減るだろうが、5八金右は「5」「八」「金」「右」と4キーで入力する方が直感的だし、3キーで確定する場合の方が多いので、「金右」の時だけ異なるボタンになるのでなく、「金」のボタンが固定される方が、慣れによる入力速度UPが期待できるので、3キー目と4キー目は分ける方が良いだろう。

同じ理由で、3キー目は、数字ボタンに固定的に
1: 歩 2: 香 3: 桂 4: 銀 5: 金 6: 角 7: 飛 8: 玉
と割り当て、成駒はその時空いている番号とする方が良かろう。それも、空いてればなるべく成る前の駒の番号(馬なら角の6)、空いてなければなるべく成る前の駒の次になる(角、馬の順になる)ように、次に空いている番号にすると良いだろう。

という訳で、夏休みにそういうiアプリを作成した。

・スクリーンショット
1キー目待ち状態の画面

3キー目待ち状態の画面

4キー目待ち状態の画面

サブメニュー画面

i-mode用ダウンロードページへのリンク

・ソースコード
 iKifuRecorder.java
 Board.java
 KifTree.java
 CGI.java

・作成者
 maruinen(筆者のネット将棋でのハンドル)

3キー目は、選択し得る駒が10種類以下なら0-9ボタンで指定、11種類以上なら0をページ切替ボタンとした。
ソフトキーで棋譜入力モード/再生モードを切り替えたり、サブメニュー画面を開いたり、アプリを終了したりできる。
棋譜の途中から入力を開始すると、元の棋譜が消えるのでなく、棋譜の分岐ができる。分岐を消すには、消したい手の先頭まで再生して、サブメニューの「ここまでの棋譜の最終手以降を消去」のボタンを押す。

入力した棋譜は、ソフトキーで終了すると、スクラッチパッドに保存される。終話キーで終了すると、保存されない。スクラッチパッドに保存された棋譜は、次回の起動時に自動的に読み込まれる。
また、入力した棋譜は、このサーバーのWebページにアップロードすることもできる。アップロードされた棋譜は、
http://ynomura.dip.jp/tmp/uploaded_kifus.html
で見ることができる。このページから棋譜をコピー&ペーストして、柿木将棋やMacの桜花で読み込めることを確認済である。
なお、棋譜をアップロードするには、このアプリの「ソフト設定」の通信設定を「する」にしてから起動する必要がある。また、このページにアップロードされた棋譜は誰でも閲覧可能だし、古いものは勝手に消えていく。

参考リンク
棋譜の表記方法:日本将棋連盟

See more ...

Posted at 14:58 in Java | WriteBacks (0)
WriteBacks

Aug 10, 2015

フィボナッチ数列の任意の項を高速に計算する方法

そんなプログラミングの問題を見かけて、反射的に思ったのは、一般項を用いることだった。
フィボナッチ数列の漸化式はan=an-1+an-2なので、一般項anを求めることは可能である。

3項を含む漸化式は、特性方程式を用いて解くのが基本であるが、やり方を忘れてしまったので、自己流で解いてみた。
a_{n+1} = a_{n} + a_{n-1}

a_{n+1} + Aa_n = (1+A)(a_n+ Aa_{n-1})
という形にできると、
(1+A)(a_n+ Aa_{n-1}) = (1+A)^2 (a_{n-1}+ Aa_{n-2}) = \cdots = (1+A)^n(a_1+Aa_0)
であることと、a1=1, a0=0であることを用いて
a_{n+1} + Aa_n = (1+A)^n
とできる。このAは、an-1の係数を見ると、(1+A)A=1となるAなので、A=(-1±√5)/2である。従って、

\left\{
\begin{array}{c}
a_{n+1} + \frac{-1+\sqrt{5}}{2} a_n = \left(1 + \frac{-1+\sqrt{5}}{2}\right)^n \\
a_{n+1} + \frac{-1-\sqrt{5}}{2} a_n = \left(1 + \frac{-1-\sqrt{5}}{2}\right)^n
\end{array}
\right.
であり、上の式から下の式を引いて整理すると、
a_n = \frac{1}{\sqrt{5}} \left( \left(\frac{1+\sqrt{5}}{2}\right)^n - \left(\frac{1-\sqrt{5}}{2}\right)^n \right) と求まる。
念の為申し上げるが、このanはnが何であろうとも整数である。実にミラクルである。

√5のn乗なんてのがあるのでは、誤差が出まくるだろうとは思ったが、CやJavaでは浮動小数点の精度が低くて無理でも、PythonのmathパッケージにMathematicaやMaxima並の驚異の性能が無いとは限らないので、試しに

#!/usr/bin/python
from math import pow
from math import sqrt

def fib(n):
    return int(1/sqrt(5)*(pow((1+sqrt(5))/2,n)-pow((1-sqrt(5))/2,n)))

an_2 = 0  #a_{n-2}
an_1 = 0  #a_{n-1}
an = 1    #a_{n}
for i in range(1, 100):
    print "n =", i, "a_n =", an, "fib(n) =", fib(i)
    if fib(i) != an:
        print "error!"
    an_2 = an_1
    an_1 = an
    an = an_1 + an_2
とやってみた所、72項目で誤差が出た。
Maximaはn>200000でも正確な整数を弾き出す(下記入出力参照、下3桁が正しいことを確認)が、さすがにPythonでは無理だった。おそらく、Maximaは内部で√5を√5として保存しながら数式処理をしてるからであろう。Pythonの言語仕様上、pow()の結果は一度float値にしないといけないので、正確な整数値を出すには無限の精度が必要になってしまう。

Maximaへの入力とその出力
(%i1) 1/sqrt(5)*(((1+sqrt(5))/2)^n-((1-sqrt(5))/2)^n),n=234567,ratsimp;
(%o1) 179607932890115544888825400217[48962 digits]014673023583001136817746498978

MathematicaやMaximaを使って良いなら、一般項を用いる方法もあるが、今回の問題は、「複数言語選択可能」としながら、MathematicaもMaximaも選択肢に無かった。 フィボナッチ数列のn項目を高速に計算する方法は、他には思い付かなかったのだが、少し調べると、計算量がO(log N)になる簡単な方法があった。

M=\pmatrix{1 & 1 \cr 1 & 0} とすると、Mn-1の{1,1}要素がフィボナッチ数列のn項目だというのが使える。
さらに、M=M*Mという計算を繰り返すことにより、M^(2k)は計算量少なく求められるので、Mn-1をM^(2k)の積とすることによって、高速に計算できる。
例えば、
M5 = 0*M8 · 1*M4 · 0*M2 · 1*M
M10 = 1*M8 · 0*M4 · 1*M2 · 0*M
であるように、2進数にした時の1のビットに対応するM^(2k)を掛け合わせれば良いのである。

従って、例えば次のようにすれば、n=10000でも高速に計算される。(Pythonで扱える整数の上限はメモリが許す限り無限である)

#!/usr/bin/python

class multipliable_2x2matrix(object):
    def __init__(self, a, b, c, d):
        self.a = a;
        self.b = b;
        self.c = c;
        self.d = d;
    def mul(self, b):
        return multipliable_2x2matrix(
            self.a * b.a + self.b * b.c,
            self.a * b.b + self.b * b.d,
            self.c * b.a + self.d * b.c,
            self.c * b.b + self.d * b.d)
    
def fib(n):
    if n == 0: return 0
    m = multipliable_2x2matrix(1, 1, 1, 0)
    f = multipliable_2x2matrix(1, 0, 0, 1)
    n = n - 1
    while n > 0:
        if n & 1 == 1:
            f = f.mul(m)
        n = n >> 1
        m = m.mul(m)
    return f.a

Posted at 10:45 in 数学 | WriteBacks (0)
WriteBacks

Jul 12, 2015

OpenGLは右手座標系?

OpenGLは右手座標系、DirectXは左手座標系である。
…色々な所にそう書いてあり、それを信じていたが、この前、自分が書いたコードが、それを否定する動作をして、混乱してしまった。
次のプログラムを実行すると、Z=0の赤い三角よりZ=-1の白い三角の方が手前に表示されてしまう。

#include <GL/glut.h>

void draw_triangle()
{
	glBegin(GL_TRIANGLES);
	glVertex3f(0, 1, 0);
	glVertex3f(-0.866f, -0.5f, 0);
	glVertex3f( 0.866f, -0.5f, 0);
	glEnd();
}

void display(void)
{
	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0, 0.0, 0.2, 1.0);  /* dark blue */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	/* red triangle at Z=0 */
	glColor4f(1.0f, 0.0f, 0.0f, 1.0f);	/* red */
	draw_triangle();

	/* white triangle at Z=-1 */
	glTranslatef(0.0f, 0.0f, -1.0f);
	glScalef(0.2f, 0.2f, 1.0f);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);	/* white */
	draw_triangle();

	glutSwapBuffers();
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
	glutCreateWindow(argv[0]);
	glutDisplayFunc(display);
	glutMainLoop();
	return 0;
}
図1
GLUTのせいか?とも思ったが、これまで自分が作ったGLUTのプログラムは間違いなく右手座標系だった。

調べた所、OpenGLの初期状態は左手座標系であることがわかった。
OpenGL Spec 2.1のAppendix B "Corollaries"の所に、

15. OpenGL does not force left- or right-handedness on any of its coordinates systems. Consider, however, the following conditions: (1) the object coordinate system is right-handed; (2) the only commands used to manipulate the model-view matrix are Scale (with positive scaling values only), Rotate, and Translate; (3) exactly one of either Frustum or Ortho is used to set the projection matrix; (4) the near value is less than the far value for DepthRange. If these conditions are all satisfied, then the eye coordinate system is right-handed and the clip, normalized device, and window coordinate systems are left-handed.
とある。ここに書かれている通り、DepthRangeがnear < farであれば、Model-View MatrixやProjection Matrixが適用された後のClip CoordinatesやNormalized Device Coordinates(NDC、X,Y,Z座標が全て-1〜+1の範囲)は左手座標系であるが、Model-View MatrixやProjection Matrixをそのように設定すれば、Object CoordinatesやEye Coordinatesを右手座標系にすることが可能なのである。
DepthRangeの初期値は、near=0、far=1であり、NDCがViewport TransformationでWindow Coordinatesになる時に、Z=0が手前、Z=1が奥と扱われるので、NDCは左手座標系である。
Projection Matrixを設定する時によく用いられる、glFrustum()やgluPerspective()は、よく見るとZ座標に掛かる係数が負の値であり、これによって、Z座標の前後関係が反転され、Eye CoordinatesではZ軸の+方向が手前、つまり右手座標系になるのである。glOrtho()を用いる場合でも、引数がleft, right, bottom, top, near, farの順なので、何も考えずにglOrtho(-1, 1, -1, 1, -1, 1);とすると、Z座標に掛かる係数が負の値になり、Eye Coordinatesは右手座標系になる。
しかし、Projection Matrixの初期値は単位行列(glOrtho(-1, 1, -1, 1, 1, -1);するのと同じ)であり、Z座標の前後関係を反転させないので、OpenGLの初期状態では、Eye Coordinatesは左手座標系である。

さて、上記の引用文にある通り、OpenGLは左手座標系(left-handed)でも右手座標系(right-handed)でも良い、とのことであるが、それでは困ることがある。特に、右手座標系だったり左手座標系だったりすると、3D物体の表面が裏返しになってしまうのが困るのである。
ポリゴンの裏表は、Window Coordinatesでポリゴンの頂点が時計回りか反時計回りかだけで決まるので、例えば、右手系であることを前提にして手前(Z軸の+方向)に表、奥(Z軸の−方向)に裏のポリゴンを配置した物体は、左手系だと手前(Z軸の−方向)に裏のポリゴン、奥(Z軸の+方向)に表向きのポリゴンがある状態になる。つまり、物体の表面が物体の内側を向いてしまう。
ポリゴンが右手系で配置されているとわかっているなら、左手系で表示するならglFrontFace()を用いて時計回りが表か反時計回りが表かを逆転させれば良いのであるが、ポリゴンが右手系で配置されているか左手系で配置されているかを考慮する必要があることが面倒である。

例えば、GLUTのオブジェクトも右手座標系を前提にしている(Teapotのポリゴンの頂点が時計回りである不具合を除く。glutSolidTeapotのman page参照)ので、CULL_FACEを有効にして左手座標系で描画すると、おかしなことが起こる。

/* showing that GLUT objects are right-handed */
void display2(void)
{
	const GLfloat lightpos_for_LH[4] = {0.0f, 0.0f, -1.0f, 0.0f};	/* directional light */
	const GLfloat default_lightpos[4] = {0.0f, 0.0f, 1.0f, 0.0f};
	
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0, 0.0, 0.2, 1.0);  /* dark blue */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_COLOR_MATERIAL);

	/* LH drawing */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-1, 1, -1, 1, 1, -1);	/* left-handed */
	glMatrixMode(GL_MODELVIEW);

	glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_POSITION, lightpos_for_LH);

	glTranslatef(-0.5f, 0.2f, 0.0f);
	glColor4f(1, 0, 0, 1);

	glRotatef(-90-20, 1, 0, 0);
	glutSolidCone(0.3, 0.7, 20, 10);

	glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_POSITION, default_lightpos);	/* to see the strangeness more clearly */
	glTranslatef(-0.5f, -0.5f, 0.0f);
	glColor4f(0, 1, 0, 1);

	glRotatef(20, 1, 0, 0);
	glFrontFace(GL_CW);	/* for glutSolidTeapot bug (see man page) */
	glutSolidTeapot(0.3);
	glFrontFace(GL_CCW);

	/* RH drawing */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-1, 1, -1, 1, -1, 1);	/* right-handed */
	glMatrixMode(GL_MODELVIEW);

	glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_POSITION, default_lightpos);
	glTranslatef(0.5f, 0.2f, 0.0f);
	glColor4f(1, 0, 0, 1);

	glRotatef(-90+20, 1, 0, 0);
	glutSolidCone(0.3, 0.7, 20, 10);

	glLoadIdentity();
	glTranslatef(0.5f, -0.5f, 0.0f);
	glColor4f(0, 1, 0, 1);

	glRotatef(20, 1, 0, 0);
	glFrontFace(GL_CW);
	glutSolidTeapot(0.3);
	glFrontFace(GL_CCW);

	glFlush();
	glutSwapBuffers();
}
図2
左半分が左手座標系で、右半分が右手座標系で同じものを描画したものである。左上の円錐は、手前の表向きのポリゴンが消えて、奥の裏向きのポリゴンが見えている。左下のティーポットは、手前から光を当てるとシルエットだけになるので、後ろから光を当てており、丁度ポリゴンの法線と光源の方向が一致して明るくなっているが、見えているのはやはり奥のポリゴンであり、所々が変である。

また、ライティングのパラメーターも、右手座標系か左手座標系かに関係する。
OpenGL Spec 2.1の記述を引用すると、lightingに関して、

All computations are carried out in eye coordinates.
とあり、例えば
The current model-view matrix is applied to the position parameter indicated with Light for a particular light source when that position is specified.
なので、Projection Matrixで右手座標系にするなら、光源の位置を含め、ライティングに関するパラメーターは右手座標系の前提で決めないといけない。
例えば、手前から光を照射する場合、右手座標系だとZ軸が+の方に光源を置くことになるが、左手座標系だとZ軸が-の方に光源を置くことになる。

ここで注目すべきは、Lighting parametersのPOSITIONの初期値は(0.0, 0.0, 1.0, 0.0)、SPOT_DIRECTIONの初期値は(0.0, 0.0, -1.0)であることだ。つまり、OpenGLの初期状態では、Z軸が+の方向から−の方向へ平行光が照射しており、スポットライトの反射方向はZ軸が−の方向であり、右手座標系を前提とした設定になっているのである。

そういう意味では、やはりOpenGLは右手座標系で使うのが基本と言えるのではないだろうか。

右手座標系の方が数学で見慣れて利便性が高いし、glFrustum()やgluPerspective()を使って右手座標系にして使うことの方が多いので、3D物体も右手座標系で作るべきだと思った。

参考URL
http://stackoverflow.com/questions/4124041/is-opengl-coordinate-system-left-handed-or-right-handed

See more ...

Posted at 23:45 in PC一般 | WriteBacks (0)
WriteBacks

Jun 28, 2015

4位

A級で4位だった。
Shoujou

4年前はB級で3位
3年前はA級で予選落ち
2年前はA級で1回戦敗退
1年前もA級で1回戦敗退、
だったので着実に進歩しているように見えるが、今年は運が良すぎた。

まず、予選が楽なグループだった。他のグループでは過去の上位入賞常連のI藤さんや、筆者が100回やっても勝てそうにないY田さんが予選敗退していたり、上位入賞常連のE島さんが黒星を付けられたり、過去出場したら半分以上優勝しているS尾さんが負けかけたりしていた中、うちのグループでは二歩で反則負けする人はいるわ、敵陣の四段目で成ったりして反則負けする人は居るわで、ポカミスだらけだった。
次に、決勝トーナメントのくじ引きで、1回戦シードのポジションを引き当てた。これだけで自己最高の2回戦進出である。
さらに、2回戦でどちらかに当たる2人の内の1人は予選で勝った相手であり、しかも1回戦ではその人が善戦していた(ポカミスで逆転負け)。決勝トーナメント14人中12番目の強さ、または全参加者中22番目の強さ(他の予選6グループの内5グループで強い人10人敗退)であれば、くじ運が良ければ4位になれてしまうのであるが、まさにそんな感じだった。

ついでに、運が良いことに、昨年までは3位までが賞状+盾だったのだが、今年は賞状だけは4位にも交付された。

See more ...

Posted at 23:59 in 将棋 | WriteBacks (0)
WriteBacks

Jun 14, 2015

[OpenGL] COLOR MATERIALの利点

OpenGLのcolor materialとは、material parameterの一部をcurrent colorに同期させるモードである。例えば、

glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); /* (1) */
glEnable(GL_COLOR_MATERIAL);
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
(some drawing)
glDisable(GL_COLOR_MATERIAL);
とすると、
GLfloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
(some drawing)
とするのと同じ効果が得られる。((1)のColorMaterialの設定は、この引数だとOpenGLのデフォルトと同じ設定なので、省略できる。OpenGL ESだと、そもそもこの関数は存在しない=ColorMaterialは常にFRONT_AND_BACK, AMBIENT_AND_DIFFUSEに働く)

これが何の役に立つのだろうか?と、ふと思った時にわからず、調べても見つからなかったので、自分なりにCOLOR_MATERIALのメリットを整理してみた。

1. 軽量("less expensive")である

Webを検索すると、いくつかのページに、color materialは"less expensive"と書かれてある。例えば、Avoiding 16 Common OpenGL Pitfallsに、

OpenGL's color material feature provides a less expensive way to change material parameters.
と書かれている。実は、筆者には"less expensive"の意味がよくわからないが、まさか
GLfloat red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
より
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
の方がコードが短くて済む、という意味ではないと思う(前者が1行で書けないのはC言語だからである)ので、ハードウェアの処理負荷が少ないという意味だと思いたい。

2. LightingがOFFの場合と同じコードになる

LightingがOFFだとcurrent colorが描画に反映され、ONだとmaterial colorが描画に反映される。Material colorをglMaterialfv()で設定すると、例えば

glBegin(GL_TRIANGLES);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red);
glVertex3f(0, 1, 0);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
glVertex3f(-0.866f, -0.5f, 0);
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blue);
glVertex3f( 0.866f, -0.5f, 0);
glEnd();
このようになるのに対し、color materialを使うと
glBegin(GL_TRIANGLES);
glColor4f(1,0,0,1);
glVertex3f(0, 1, 0);
glColor4f(0,1,0,1);
glVertex3f(-0.866f, -0.5f, 0);
glColor4f(0,0,1,1);
glVertex3f( 0.866f, -0.5f, 0);
glEnd();
のように、lightingがOFFでもそのまま使えるコードにすることができる。

3. Color arrayの色をmaterialに反映できる

glBegin()〜glEnd()とするのでなく、頂点配列を用意してglDrawArrays()やElements()を使って描画する場合、頂点毎にmaterial colorを設定するには、color material+color arrayを用いるしかない。

一応、GLUTのサンプルコードを作ってみた。
color_material_test.c
display()のcase 1〜7(左クリックで出るメニューのTest1〜7)は、それぞれ、以下の内容である。

  1. Material colorをglMaterialfv()で設定する例(glBegin()〜glEnd()使用、case 3まで同様)
  2. Material colorをcolor materialで設定する例
  3. 同じコードがlighting OFFでも使えることを示す例
  4. Material colorをglMaterialfv()で設定する例(glDrawArrays()使用、以下同様)
    全ての頂点が同じ色になる。
  5. Material colorをcolor materialで設定する例(color array不使用)
    case 4と同じ表示になる。
  6. Material colorをcolor materialで設定する例(color array使用)
    Color materialを使用すれば、頂点毎にmaterialを変えられる。
  7. 同じコードがlighting OFFでも使えることを示す例(color array使用)
case 0(初期状態)は、Color materialを使用しないと、current colorもcolor arrayも反映されないことを示す例である。(灰色の四面体が表示される。)

実行例
case 1-3 case 4-5 case 6
左から、case 1-3、case 4-5、case 6の画面

See more ...

Posted at 23:02 in PC一般 | WriteBacks (0)
WriteBacks

Mar 12, 2015

[JIRA] 作成できる課題タイプをグループによって制限したい

JIRA 6.2で、同じプロジェクトで2種類のバグ票を運用したいと思った。
通常、JIRAのバグ票は、そのプロジェクトの関係者(のRole)をAdministrators, Developers, Usersと分ける時、Usersが起票し、Developersが処理する。それに加えて、今回は、Developers+α("group1"に所属するメンバーとする)だけが起票可能、閲覧可能な、開発者側の内部バグ票("IT Bug"とする)を、通常のバグ票とは別の課題タイプで運用したかった。
※結合テスト=Integration Testで発見された不具合、またはInternal Bugの略

特定の課題タイプを、特定のグループのメンバーだけが閲覧可能にするのも、直接的な設定方法が無く、容易ではないが、例えば、
  • Issue Security Schemeのセキュリティレベル定義を、default=Private(group1のみ閲覧可能)にする
  • Set Issue Security権限を課題作成可能者全員に与える
  • 画面にSecurity Levelフィールドを設ける
とすると、group1が開いた課題作成画面ではセキュリティレベルがPrivateになり、それ以外のメンバーが開いた課題作成画面ではセキュリティレベルがNoneになる(Noneは制限無し、設定権限があるのにNoneしか選べないからそうなる)ので、IT Bug以外は手操作でセキュリティレベルをNoneにすれば、何とか近いことが実現できる。
かなり強引で多少面倒だが、通常のバグ票を開発者が起票することがあまり無く、あっても非公開にしたいことがあり得るとすれば、現実的には妥当な解と言えなくはない。
(ちなみに、画面にSecurity Levelフィールドを設けなければ、全ての課題のセキュリティレベルがPrivateになる。これがNoneになるなら、IT Bugの作成画面だけにSecurity Levelフィールドを設ければ、IT Bug以外は自動的に全員閲覧可能にできるのだが、そうはならない。また、IT Bugのワークフローだけ、Create IssueトランジションのPost FunctionsにてSecurity Level=Noneにできれば良いが、JIRA 6.2にはその関数が無く、有料のプラグインをインストールしないとできない。)

しかし、特定の課題タイプは、特定のグループのメンバーだけが課題を作成可能にすることは、次の理由で、さらに難しい。

  • Create Issue権限を課題タイプ毎に別にできれば良いだけなのだが、Permission Schemeは、課題タイプ毎には設定できない。
  • 課題タイプ毎にworkflowは別にできるので、Create IssueトランジションのConditionsで制限すれば良いだけなのだが、なぜかCreate IssueトランジションにはConditionsが無い。
  • Create IssueトランジションにValidatorはあるが、グループではなく、何らかのPermissionしか指定できない(そのPermissionが無いというエラーになるので、Create Issue権限以外を指定すると意味不明なエラーになってしまう)し、作成ボタンを押した後にしかエラーにならない。
  • Workflow propertiesで制限できれば良いが、"(you can use in) Step"と書いてあり、実際に試してみたが、jira.permission.*はtransitionに対しては無効だった。Create Issueをする前には状態(Step)が無いので、jira.permission.*を設定する術が無い。
  • 同じプロジェクトで、課題タイプ毎に変えられる設定というのは、ワークフロー、画面、フィールド設定と、カスタムフィールドの初期値くらいしか見当たらない。

JIRA関連のドキュメントでもGoogleでも、"Permission per Issue Type"で検索して辿って行くと、大体、次の課題票に行き着く。
[JRA-5865] Allow permission schemes to be configured per issue type - Atlassian JIRA
約10年前に出され、多くの人に必要性を語られ、継続的に議論されてきた要望だが、JIRAを開発するAtlassian社によって、"Won't Fix"として昨年7月にcloseされている。
1つのプロジェクトで、課題タイプ毎に権限を変えるようなことはするな、という意味にしか取れない。
しかし、同じプロジェクトで、どの帳票も起票するメンバー、処理するメンバーが同じ、という制限では不便である。
例えば、発注者がバグ報告をする場合、開発者でない発注者が担当者になることは無いので、バグ票のAssigneeの権限は開発者に限定したいが、開発者が発注者に質問する場合、発注者が担当者になるので、Q&A票のAssigneeの権限は発注者に限定したい。
しかし、それだけのことすら、JIRAでは可能にす対応する予定が無いという。
プロジェクトを別にすれば良い、ということかも知れないが、課題タイプ毎にプロジェクトを別にするのでは課題タイプの意味が無いし、JIRAでプロジェクトを別に立ち上げるのは、バージョンやロールなど、共有できないので二重管理になるものもあるし、カスタムフィールドなど、プロジェクトに依存する設定もあり、結構面倒である。

帳票毎に権限を制約できないが為に、例えばAssignee権限を広くすると、その帳票の担当者になり得ないメンバーに間違ってassignしてしまう可能性があるし、一部の帳票は閲覧を制限するように課題にセキュリティレベルを設けていると、間違ってassignされた人がその課題を参照できず、課題が行方不明になってしまう。
今回やりたいことは、課題タイプが"IT Bug"の課題は、開発受注者のグループしか作成できないようにすることである。これも、帳票毎に権限を制約できないが為に、発注者も"IT Bug"を作成可能にすると、発注者にとっては、起票時に知らない課題タイプが選択肢に出て来るのがいまいちだし、間違って"IT Bug"で起票されてしまうと、いちいち開発者側で課題タイプを修正するのが面倒である。
それだけの為に、"IT Bug"を別プロジェクトにするのも、管理が面倒だし、見苦しい。

どうしても諦められなくて、上記の課題票JRA-5865のコメント欄を読むと、初期の頃から、JavaScriptでCreateボタンを消すことによる暫定対策が検討されており、貼られているJavaScriptを試してみると、JIRA 6.2でも部分的には動いたので、JavaScriptで何とかできないかを追究してみることにした。
なお、筆者にはJavaScriptの知識はほとんど無い。documentクラスでHTMLを操作する方法も、windowクラスでWebブラウザを制御する方法も知らない。AJS(Atlassian JavaScriptライブラリ) 頼みであり、AJSのドキュメントが無いのでGoogle頼みである。

■JIRAでのJavaScript使用の基本

フィールドのDescriptionにJavaScriptを書くと、そのDescriptionが表示される画面で実行される。
例えば、次のテキストをSummaryフィールド等のDescriptionに貼り付けると、課題作成画面を開く時に"JavaScript ran!"という警告ダイアログが出るようになる。

This description has some JavaScript.
<script language="JavaScript">
<!--
alert("JavaScript ran!");
//-->
</script>
Announcement Bannerに含めても実行されるが、scriptタグ以外に何も無くても、細いAnnoucement Bannerが表示されるようになってしまう。
参考:Fields Allowing Custom HTML or JavaScript - Atlassian Documentation
なお、 JIRA 6.2.x以降、これらのJavaScriptを埋め込む方法の一部は使えなくなる、と書いてあるが、JIRA 6.2.7では上の方法でJavaScriptが実行されることを確認した。

AJSを使用する場合は、実行されるタイミングが問題になるので、次の例の(function($) { ... })(AJS.$);の部分のように書くのが定跡のようである。

<script language="JavaScript">
(function($) {
  AJS.toInit(function(){
    //(A)
    alert("init on load: current user = " + AJS.params.loggedInUser);
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    //(B)
    alert("init on refresh: current user = " + AJS.params.loggedInUser);
  });
})(AJS.$);

//(C)
alert("outside function: current user = " + AJS.params.loggedInUser);
</script>
これをSummaryフィールドのDescriptionに埋め込むと、次のことがわかる。
  • 通常の(ポップアップでない)課題作成画面が開く時に(A)が実行される。
  • 通常の課題作成画面が開く時は(C)の位置ではAJSが働かない。
  • ポッブアッブ形式の課題作成画面が開く時に(A)が実行される。
    その前に(B)が呼ばれることもある。(元のページによっては、例えばClosedでない課題画面は、開いた途端に(A)が呼ばれ、(B)のコールバックも登録されるので、Create Issueボタンを押してポップアップ課題画面が開いた途端に(B)が呼ばれる。)
  • ポッブアッブ形式の課題作成画面でプロジェクトや課題タイプを変えると(B)が呼ばれる。
なお、ポップアップ課題作成画面を開く度にコールバックが追加されるので、元の画面をそのままに、何度もポップアップ課題作成画面を開くと、開く度に(B)が何度も呼ばれるようになってしまうが、これは仕方が無さそうだ。

ポップアップ課題作成画面におけるJavaScript実行について

ポップアップ形式の課題作成画面は、開いた状態でプロジェクトや課題タイプが切り替えることが可能で、それらの切り替えに連動してフィールドが出現したりDescriptionが変化することがあるが、切り替えによって表示される新たなDescriptionのJavaScriptは実行されない。従って、ポップアップ課題作成画面で実行されるべきJavaScriptは、ポップアップ課題作成画面が開く時にどのプロジェクトのどの課題タイプの画面から始まっても、必ず実行されるようにする必要がある。
例えば、全てのフィールド設定において、いずれかのDescriptionにJavaScriptを含める方法や、Announcement Bannerに含める方法が考えられる。
今回は、あらゆるプロジェクトのあらゆる課題タイプでSummaryフィールドが表示されるものとして、全てのフィールド設定のSummaryフィールドのDescriptionにJavaScriptを埋め込んでテストした。

■作戦1

該当プロジェクトの、作成が制限された課題タイプなら、Createボタンをdisabledにする。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
    AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = value;
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var project = AJS.$("#issue-create-project-name").text();
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (isUserInGroup('group1') == false){
    var project = AJS.$("#project-field").val();
    var issueType = AJS.$("#issuetype-field").val();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    } else {
      disableCreate(false);
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

isUserInGroup()は、Webのどこかから拾ったものである。
disableCreate()は、JRA-5865のコメント欄にあるものを、display='none'(非表示)でなくdisabled=true(グレーアウトして無効化)にするよう変えたものである。Createボタン(type="submit"のINPUTタグ)を非表示にしても、いくつかのブラウザではSubmitのショートカットキー(ChromeやIEではAlt+S、FirefoxではAlt+Shift+S、SafariではCtrl+Shift+Sなど)は効いてしまうため、disabledにした。(ChromeとSafariは効く、FirefoxとIE8は効かない)
initCreateIssueScreen()は、通常の(ポップアップでない)課題作成画面のCreateボタンの処理である。ユーザーが'group1'に属さず、プロジェクトが'Test Project 1'で、課題タイプが'IT Bug'なら、Createボタンをdisabledにしている。
initPopUpCreateIssueScreen()は、ポップアップ形式の課題作成画面のCreateボタンの処理である。ユーザーが'group1'に属さず、プロジェクトが'Test Project 1'で、課題タイプが'IT Bug'なら、Createボタンをdisabledに、そうでなければ、enabledにしている。
AJS.toInit()の部分は、このスクリプトの中で最初に実行される処理である。課題作成画面が開く時にも実行される。通常の課題作成画面ならinitCreateIssueScreen()を、ポップアップ課題作成画面ならinitPopUpCreateIssueScreen()を呼び出している。
JIRA.bind()の部分は、同じページで画面が変化すると実行される処理である。現在のページがポップアップ課題作成画面なら、initPopUpCreateIssueScreen()を呼び出している。
なお、プロジェクトIDや課題タイプIDを取り出す方法がわからなかったので、プロジェクトや課題タイプは、IDではなく実際に表示される文字列を比較に使っている。これらの文字列は変更される可能性があるので、このやり方はあまり好ましくない。この方法だと、課題タイプは多言語訳の登録が可能なので、登録した全言語の文字列と比較するようなことも必要になるが、ここでは省略している。

結果

通常の課題作成画面では、作成が制限される条件であれば、うまくCreateボタンが無効化される。
ポップアップ形式の課題作成画面でも、開いた時はCreateボタンが無効化されるが、開いたままプロジェクトや課題タイプを切り替えると、disableCreate(true)が呼び出されても、Createボタンが有効になってしまう。(Safari, Firefox, Chrome, IE全て同様)
元々、ポップアップの課題作成画面では、プロジェクトや課題タイプを切り替えると、Createボタンが一時的に無効になって有効に戻るので、この有効にする処理が後から走ってしまうのだと推測される。
色々試したが(後述)、最終的にCreateボタンをdisabledにする適当な方法は見つからなかった。
但し、Createボタンを非表示にすると、その状態は維持されることがわかった。

■作戦2

通常の課題作成画面では、該当プロジェクトの、作成が制限された課題タイプなら、Createボタンをdisabledにする。
該当プロジェクトの、作成が制限された課題タイプでは、Createボタンをdisabledかつ非表示にする。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
    AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = value;
    }
  }
}

function hideCreate(value) {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      if (value == true) {
        inps[i].style.display = 'none';
      } else {
        inps[i].style.display = '';
      }
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var project = AJS.$("#issue-create-project-name").text();
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (project == 'Test Project 1' && issueType == 'IT Bug') {
      disableCreate(true);
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (AJS.$("#create-issue-dialog").length) {
    if (isUserInGroup('group1') == false){
      var project = AJS.$("#project-field").val();
      var issueType = AJS.$("#issuetype-field").val();
      if (project == 'Test Project 1' && issueType == 'IT Bug') {
        disableCreate(true);
        hideCreate(true);
      } else {
        disableCreate(false);
        hideCreate(false);
      }
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

作戦1のスクリプトのinitPopUpCreateIssueScreen()に、hideCreate()の呼び出しを加えた。。
hideCreate()は、Createボタンをdisplay='none'にする処理である。

結果

作戦1同様、通常の課題作成画面では、作成が制限される条件であれば、Createボタンが無効化される。
ポップアップ形式の課題作成画面では、意図通りに、作成が制限される条件ならCreateボタンが消え、そうでなければCreateボタンが出現する。画面を開いたままプロジェクトや課題タイプを切り替えると、条件に従ってCreateボタンが出たり消えたりする。
但し、開いた直後にCreateボタンが消えていればSubmitのショートカットキーも効かないが、開いた後にプロジェクトや課題タイプを切り替えると、Createボタンが消えても、作戦1と同様、ショートカットキーは効いてしまう。

■作戦3

通常の課題作成画面では、作成が制限された課題タイプなら、Createボタンをdisabledにする。
ポップアップ課題作成画面では、課題タイプの選択肢から、制限された課題タイプを削除する。
※プロジェクトを問わず、その課題タイプの使用を制限する。その理由は後述。

<script language="JavaScript">
<!--
function isUserInGroup(group){
  var groups;
  AJS.$.ajax({
    url: AJS.params.baseURL + "/rest/api/2/myself?expand=groups",
    type: 'GET',
    dataType: 'json',
    async: false,
    success: function(data) { groups = data.groups.items; }
  });
  for (i = 0; i < groups.length; i++){
    if (groups[i].name == group){ 
      return true;
    }
  }
  return false;
}

function disableCreate() {
  var inps = document.getElementsByTagName('INPUT');
  for (i = 0; i < inps.length; i++) {
    if (inps[i].type == 'submit') {
      inps[i].disabled = true;
    }
  }
}

function hideITBug(){
  var ops = AJS.$("#issuetype option");
  for (i = 0; i < ops.length; i++) {
    if(ops[i].text == 'IT Bug'){
      AJS.$("#" + ops[i].id).remove();
      return;
    }
  }
}

function initCreateIssueScreen() {
  if (isUserInGroup('group1') == false) {
    var issueType = AJS.$("#issue-create-issue-type").text();
    if (issueType == 'IT Bug') {
      disableCreate();
    }
  }
}

function initPopUpCreateIssueScreen() {
  if (AJS.$("#create-issue-dialog").length) {
    if (isUserInGroup('group1') == false){
      hideITBug();
    }
  }
}

(function($) {
  AJS.toInit(function(){
    if (location.pathname.indexOf("CreateIssue") != -1) {
      initCreateIssueScreen();
    }
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  })

  JIRA.bind(JIRA.Events.NEW_CONTENT_ADDED, function (e, context) {
    if (AJS.$("#create-issue-dialog").length) {
      initPopUpCreateIssueScreen();
    }
  });
})(AJS.$);
//-->
</script>

スクリプトの説明

作戦1のスクリプトのinitPopUpCreateIssueScreen()は、hideITBug()を呼び出すように変えた。
hideITBug()は、課題タイプのプルダウンメニューから'IT Bug'のオプションを削除する処理である。
また、全体的にプロジェクト名の比較は無くした。
その結果、disableCreate()の引数は不要になったので、無くした。

補足説明

このようにして課題タイプの選択肢から'IT Bug'を削除しても、ポップアップ画面が開く時の初期値が'IT Bug'だったり、ポップアップ画面で別プロジェクトにして'IT Bug'を選択してからプロジェクトを切り替えると、課題タイプが'IT Bug'になってしまう。その為、課題タイプの選択肢から削除する方針なら、全プロジェクトにおいてその課題タイプを制限する必要がある。
通常の課題作成画面に入る前の、プロジェクトと課題タイプを選択する画面では、フィールドのDescriptionが表示されないので、JavaScriptによる課題タイプの制限が難しい。その為、通常の課題作成画面では、Createボタンをdisabledにしている。

結果

作戦1同様、通常の課題作成画面では、作成が制限される条件であれば、Createボタンが無効化される。
ポップアップ形式の課題作成画面では、意図通りに、課題タイプの選択肢から'IT Bug'が無くなる。
しかし、デフォルトの課題タイプを'IT Bug'にしていると、ポップアップ画面で'Test Project 1'に存在しない課題タイプを選択した状態から'Test Project 1'に切り替えると、課題タイプが'IT Bug'になってしまう。従って、例えば結合テストのフェーズではデフォルトの課題タイプを'IT Bug'にしたくても、それができないことになる。デフォルトの課題タイプを'IT Bug'にし得るなら、作戦2のように、その時にCreateボタンを非表示かつdisabledにする必要がありそうだ(それでも、作戦2と同じく、ショートカットキーによるSubmitまでは止められない)。
また、この方針だと、通常の課題作成画面の手前の課題タイプ選択画面では相変わらず'IT Bug'が選択できてしまうのが、統一感が無くて不満である。

結論

いずれの作戦もデメリットがあるし、全プロジェクトに影響してしまうので、特定の課題タイプの課題の作成をJavaScriptで制限するのは諦める。

面倒でもプロジェクトを分けるか、どうしてもプロジェクトを分けたくなければ、エラーメッセージがわかりにくくなるが、Create IssueトランジションのValidatorsで作成者を制限するのが最善だと思われる。
なお、Create IssueトランジションのValidatorsは、ワークフロー編集画面のDiagram版から開くことができる。Validatorとしてはいずれかの権限しか選択できないので、いずれの課題タイプについても内部バグ票の作成者に制限しても良さそうな権限、例えばSchedule Issues権限をを選んでそれを内部バグ票の作成可能者に制限し、内部バグ票のCreate IssueトランジションのValidatorにも使用する。

See more ...

Posted at 18:25 in PC一般 | WriteBacks (0)
WriteBacks

Jan 28, 2015

男性不妊因子の治療

筆者は、7月に精液検査というものをして、受精に繋がる精子がほぼ0という結果が出てしまった。 特に、受精の成功率に大きく関係するとされる、"PMSC A"の精子量が、下の表のように、0.0〜0.3×106/mlと極端に最低基準を下回っていたのである。
精液の質は日々の体調によって大きくばらつくので、結果が悪い場合は1ヶ月以内に再検査するのだが、それでも悪かった。

不妊治療(ART)では、自然妊娠がうまくできなければ、人工受精(AIH) → 体外受精(IVF) → 顕微授精(ICSI)、と順に「ステップアップ」する手段があるのだが、その最後の手段である顕微授精でも相当困難になるという、絶望的な数値であった。

男性不妊の原因の9割とされる「造精機能障害」である。
それから治療を続けた結果、半年でかなり改善したので、経緯を報告する。

検査結果の推移
検査項目WHOの
旧基準値
WHOの
新基準値
検査日単位
2014/7/47/119/2011/52015/1/17
精液量2.0以上1.5以上1.02.21.52.94.7ml
精子濃度20.0以上15.0以上12.59.41.717.783.0106/ml
運動精子濃度(MSC)1.41.61.39.044.8106/ml
高速前進(PMSC) A5.0以上0.00.31.26.441.5106/ml
低速前進(PMSC) B0.00.10.01.61.7106/ml
運動率(A+B+C)50以上11.217.0775154%
運動率(A+B)40以上0.04.3714552%
直進運動性 A25以上0.03.2713650%
直進運動性 B0.01.1092%
直進運動性 C11.212.7662%
精子自動性指数(SMI)80以上08???
※この間、年齢は40歳であった。
 7/11以前の検査は婦人科での検査。結果用紙の「高速前進(PMSC) A」が赤字(強調)であった。
 9/20以降の検査は泌尿器科での検査であり、検査項目が少ない。

以前から、精力というか男性ホルモンが低下してるっぽい自覚症状はあった。
35歳くらいから、いわゆる朝立ちが全く無くなった。
勃起力も次第に弱くなり、38歳くらいからか、完全に勃起することがほとんど無くなった。
2回目の鬱病のピークが34歳の頃で、その頃から、性欲が急速に低下した。
心療内科で処方された薬の中には、EDを引き起こす可能性があるとされるもの(ジェイゾロフト)が含まれており、4年後の症状がほぼ無くなった頃に、その薬から服用を停止したが、その薬の影響が無くなるとされる2ヶ月が経っても、何も改善しなかった。
なお、1回目の鬱病のピークは29歳の時で、その時はジェイゾロフトは飲まなかった。

1年前から、EDの治療を意識し、泌尿器科に相談して薬剤を処方してもらったり、独自に様々な取り組みをしたが、改善は見られなかった。その流れで、婦人科にて精液検査を受けた結果、冒頭の「特発性造精機能障害」が判明し、再度、泌尿器科に相談した。

不妊症とは、医学的な定義としては、正常な性交渉を2年間継続して妊娠に至らなければそう診断されるものである。その内、男性に(も)原因があるものを、男性不妊症と呼ぶ。 (筆者の場合、性交渉に至っていないので、厳密には男性不妊症には当たらない。)
男性不妊の原因の9割が造精機能障害であり、6割は原因不明の「特発性造精機能障害」とされ(参考文献[1])、改善する為の確実な方法は知られていない。また、研究の結果、原理的に特効薬は無いことが確実とされている(参考文献[2])。
造精機能障害には、次のような種類がある。

乏精子症
精子濃度がWHO基準値未満
高度乏精子症
精子濃度が5×106/ml未満
無精子症
精子濃度が0
精子無力症
精子運動率がWHO基準値以下

(目安として、ある病院では、大体、精子濃度が20〜30×106/ml以下なら人工受精、3×106/ml以下なら体外受精、1×106/ml以下なら顕微授精が適当、という基準にしているらしい[1]。 婦人科の医師からは、タイミング法等によって自然妊娠を目指すには、精子濃度が40×106/mlはあることが望ましいと言われた。当然、大半の男性の精子濃度はそれを上回っているし、運動率も50%を上回っている。)

これらの内、無精子症以外の場合、まず行われるのは、漢方やビタミン剤(B12やC)の処方である[1]。 処方される漢方薬には、「補中益気湯」や「八味地黄丸」などがある[2]。
補中益気湯
精子運動率の改善作用があるとされる。
八味地黄丸
精子数の増加作用があるとされる。
精子運動率が改善することもあるとの情報あり。
地黄というのがお腹に強烈らしく、腹痛を起こすこともある。

筆者は「乏精子症」と「精子無力症」に該当し、補中益気湯が処方された。
精子の形成は70日以上かかるので、漢方薬の服用は3ヶ月は続けるとのことだった。
それから、普段の生活で気を付けるべきこととして、以下を確認した。

  • ストレスが大敵
  • 適度な運動をする、但し股間を圧迫する自転車は好ましくない
  • 股間を暖め過ぎない、膝上でのノートパソコン使用は良くない
  • 喫煙は良くない
  • 薬によっては影響することもある
  • 禁欲期間は長過ぎないこと
  • バランスの良い食事
ネットでは、コーヒーの飲み過ぎも悪影響をもたらすようなことが書いてあったので、医師に聞いてみたが、それは関係ないとのことだった。

これを受けて、7/19から補中益気湯を飲み始め、それから2ヶ月くらい、雨の日以外は毎晩欠かさず3km程度のウォーキングを行った。
ストレスは、仕事をセーブするなどして、軽減するように努めた。
膝上でのノートパソコンや喫煙は、筆者は元々していない。問題となった薬も既に停止していた。
コーヒーは特に関係ないとのことだったが、念の為、毎日800ccは飲んでいたのを、500cc程度に減らした。

食事については、参考文献[3]に、以下の栄養分が必要と書いてあった。

  • ビタミンE
  • ビタミンB12
  • 亜鉛
  • セレン
  • アルギニン
元々、納豆を週5日、魚も週3日以上は食べていたので、ほぼ不足はしていないと思ったが、アルギニンの摂取量には自信が無かったので、すりごまを毎日食べるようにした。

なお、11月以降は寒くてウォーキングを中断したが、しばらくすると勃起力が下がった実感があり、少し再開するとすぐ回復した感じがした。運動の有無が調子を大きく上下させると感じたので、週末の昼間になるべく3kmくらいは歩くようにした。

その結果、2ヶ月後のデータは運動率から改善したと読んで良いかどうかわからない(この時だけ精子濃度が極端に低いので、検査対象の採取に問題があった可能性がある)が、4ヶ月後には精子濃度、運動率共に、WHOの新たな最低基準値を上回り、6ヶ月後には造精機能障害を脱したと言える水準まで回復した。

See more ...

Posted at 23:10 in 雑記 | WriteBacks (0)
WriteBacks