2019年4月28日日曜日

影が重なる時に伸びるように見えるやつ


 影と影が重なる時に一方の影が伸びるように見えたり縞模様ができたりするのがすごい好きで暇つぶしによく使っていたのだが、これってプログラムで再現できるかな?とふと思ったのでやってみた。
 感覚一辺倒で「はっきりした影の外側のぼや~っとした影が重なることからなんやかんやでいろんな風に見えるんやろ」と思っていたことから、(1)光源に大きさがあること (2)光源が複数存在すること が影響していると予想し、色々考えた結果次の4パターンの再現を試みた。

(1)大きさ無限大の連続した光源
(2)大きさ有限の連続した光源
(3)一定間隔で無限に並ぶ点光源
(4)一定間隔で有限に並ぶ点光源

 なお3次元空間で考えるのは大変なので、2次元平面で考えた。

[設定]
 床と高さHの天井がある。-∞~0に高さh1で板P1が、a~∞に高さh2で板P2があり、いずれも水平で厚さはない。またh1>h2とする。
 天井のある点yが発する光が点xを照らす強さf(x, y)は、入射角θとした時のsinθに比例し、その点間の距離で求められる球の表面積Sに反比例するものとする。つまり定数lを用いて、f(x, y)=(l*sinθ)/Sとする。
 光源が連続の場合、天井の-d~dは発光している(蛍光灯がある)とする。光源が点の場合、-d~dの範囲の整数の点が発光している(電球がある)とする。無限に光源が続いていることを仮定する場合、d=∞とする。
 ごちゃごちゃ書いたけど図にすればこれだけのことです。


[実装にあたり]
・点xを照らす点yの条件を計算すると-x*(H-h1)/h1<y<x+(a-x)*H/h2なので、f(x, y)をこの範囲でyについて積分すると、点xの明るさI(x)が求まる。
・I(x)を計算するxの範囲は-1<x<h1*max(a)/(h1-h2)とした。上限は絶対に光が差さない領域との境界。下限は特に理由はないが、感覚としてx<0では特に面白いことは起きなさそうだと思ったため適当に決めた値である。
・yを与えられたときに|y|<dならlを、そうでなければ0を返す関数light_existをf(x, y)の定数lに突っ込むことで、汎用性をあげた。

[結果確認]
 結果はヒートマップ形式で見る。マス目の値の弄り方をググるのが面倒だったので、対応させていない点に注意。最上段はa=0であり、下に行くにつれてaが大きくなる(つまり板P2を右へ移動している)。左右はxの値であり、左端がx=-1、右端がx=h1*max(a)/(h1-h2)である。

(1)大きさ無限大の連続した光源


 上右端より上中ちょい左の方が暗いことになっているのさすがに解せないし、しかもその領域は明るさ0を下回っているあたりダメそう。その部分を華麗に無視して明るいところのパターンを見ても、そこまで面白い部分は見つからない。

(2)大きさ有限の連続した光源







 やはり無限の場合と同じく、0未満の領域が気になる。ダメそう。

(3)一定間隔で無限に並ぶ点光源


 直感的に縞模様はこれで納得できますな。また、この図の一部に具体的な数値を入れてみると、


となり、aが小さくなるとそれまで明るかった部分が一気に暗い側に近い値になることで、影が伸びるように見えると考えることもできそうである。

(4)一定間隔で有限に並ぶ点光源







 点光源が増え影の境界が増えることにより、シマシマも増えるってな具合ですな。

 と、こんなんで一応の解決を得たっぽいのだが、連続光源の場合の結果が気持ち悪い。今デスクスタンド(≒有限の連続光源)でマクロに実験してみたところ影は伸びるように見えたので、どうも再現に失敗しているようである。
 さらには、(2)の場合でdのオーダーを下げてみたところ次のようになった。





 この結果は、aを大きくしていくと、明るい部分にぼつぼつと影が出現し始める、ということを意味する。波動の干渉とかならともかく、今回の例でこれはナンセンス。このようにおかしな結果が得られたことに加え、明るさが負になったりしたあたりなども含めて、integrate.quad関数あたりが影響しているような気がする。ぼつぼつの影については、積分区間に対してdが小さすぎることで、ステップ幅的に光源の領域をスキップし、積分値がゼロになってしまったのではないか。その他なんやかんやで反意図的に負の値が出てきたのではないかなぁ~と思ったあたりで疲れたので今日はここまで。

2019年4月21日日曜日

英文をコピペでgoogle翻訳に突っ込む時、改行コードとかが邪魔なので一括で消したい

 この記事のソースコードはこちら。

 改行コードはtext.replace('\n', '')で消せた。
 改行コードの他に消したいものを考えたら、文章中でreferenceがその都度括弧で記載されていて読みにくい時があるのでそれも消したい。どうやらreモジュールで正規表現を使えるようなので使います。

def fixer(text):
    #改行コード削除
    text=re.sub('^\n', '', text)
    text=re.sub('\n$', '', text)
    text=text.replace(' \n', ' ')
    text=text.replace('\n', ' ')
 
    #括弧削除
    text=re.sub(' \(.*?\)', ' ', text)
    text=re.sub('\(.*?\)', '', text)
 
    #カンマ・ピリオド前の空白削除
    text=text.replace(' ,', ',')
    text=text.replace(' .', '.')
 
    return text


 正規表現で最短一致列が欲しい時には?マークを使うのは知らなかった。
 なにはともあれ正規表現で削除したいものを削除できるようになった。これで他にもいろいろ消したいものが出てきたときは消せそうです。

 上の関数を使って、コピペしたい文章をその都度jupyterに貼り付けてRun、でもいいのだが、もうちょっと使い勝手良くしたいなと思ったので、tkinterでGUIにした。

 paster.pyを実行すると、ただの2枠と中央のボタン1個のウインドウが出てくる。左に元の文章コピペして矢印ボタン押すと、右枠に処理済のテキスト出てくるようになっている。


 なおpyperclipをインストールしてあれば、paster_clip.pyが使える。こっちだと矢印ボタンを押した時点でクリップボードに処理済みテキストがコピーされる。

 今後の課題は以下。
・括弧は一括で削除しているが、本当はreferenceっぽいものだけを選択的に削除したい。正規表現の中に年号を含むようにすればいけそうではある。
・ボタン押さなくても変換してほしい。
・googletransというモジュールがある。これ使えば自動で翻訳してくれるような気もするが、インターネットで翻訳した方が精度が良さそうな気もする。そのあたりの検証も含めてgoogletransは近日中に触りたい。