Arduboyで狙い撃つぜ!(atan2テーブルの作り方) その2
例によって例のごとく、atan2は重い。
テーブル化したいのだが……。
その前に、atan2について調べてみた。
ターゲットを狙い撃つ場合、そのターゲットまでのx,yの距離は分かる。
であれば、tanの逆関数であるatanを使えば良さそうだが、
x=0のとき、つまり、90度、270度の時にゼロ割が発生してしまう。
atanだと90度、270度の時に場合分けが必要になって、使いづらい。
atan2はその辺りが考慮されており、単にyとxを与えれば、角度が返ってくる。
なんて素晴らしい。
そこで、atanでatan2と同じような動作を再現して、中身を想像してみた。
atanは、-PI/2〜PI/2ラジアンを返すので、(-90〜90度ということ)
- xが正の場合は、atanの値をそのまま使う。
- xが負の場合は、yの符号をチェックして、第2象限、第3象限に場合分け。
- 90度、270度、x=0かつy=0の場合は、特殊処理。
ということになった。
Pythonで動作を確認してみた。
# PI/2 # | # atan(y/x)+PI | atan(y/x) # | # PI --------------+-------------- 0 # | # atan(y/x)-PI | atan(y/x) # | # -PI/2 import math def atan2(y, x): if x > 0: # 第1、第4象限 return math.atan(y / x) elif x < 0: if y < 0: # 第2象限 return math.atan(y / x) - math.pi else: # 第3象限 return math.atan(y / x) + math.pi else: if y > 0: # 90度 return math.pi / 2 elif y < 0: # 270度 return -math.pi / 2 else: # x,yが0 return 0
以下のようにして、
数学ライブラリのatan2と自作のatan2を比較してみた。
# PI/2 # | # atan(y/x)+PI | atan(y/x) # | # PI --------------+-------------- 0 # | # atan(y/x)-PI | atan(y/x) # | # -PI/2 import math import random import sys def atan2(y, x): if x > 0: # 第1、第4象限 return math.atan(y / x) elif x < 0: if y < 0: # 第2象限 return math.atan(y / x) - math.pi else: # 第3象限 return math.atan(y / x) + math.pi else: if y > 0: # 90度 return math.pi / 2 elif y < 0: # 270度 return -math.pi / 2 else: # x,yが0 return 0 # テストコード for i in range(100000): x = random.randint(-sys.maxsize-1, sys.maxsize) y = random.randint(-sys.maxsize-1, sys.maxsize) v1 = math.atan2(y, x) v2 = atan2(y, x) # v1,v2の値が異なっていれば表示する(わずかな誤差は許容) if round(v1 - v2, 15) != 0: print(str(x) + "\t" + str(y))