tangent lines from a point to a convex polygon
概要
ある点 \(w\) から凸多角形 \(P = (v_1, v_2, ..., v_N)\) への接線を引いた時に、その接線を通る凸多角形の点 \(v_i\) を求める。
この時、点 \(w\) は凸多角形 \(P\) の外側に存在するとする。
具体的計算
凸多角形 \(P\) 内の適当な点 \(s\) を選ぶ。
\(h(i)\) を 点 \(w\) と \(s\) を通る直線と点 \(w\) と \(v_i\) を通る直線がなす角 (\(h(i) \in [-\pi, \pi]\)) と定義する。
関数 \(h\) の最大値をとる \(x\) を \(x_1\), 最小値をとる \(x\) を \(x_0\) とする時、 この関数は以下の性質を満たす。
-
区間 \([x_0+1, x_0+N-1]\) について 関数 \(h\) は上に凸な関数
-
区間 \([x_1+1, x_1+N-1]\) について 関数 \(h\) は下に凸な関数
この最大値と最小値は2回の三分探索で求める。 1回目の三分探索では 区間 \([1, N]\) で定義される以下の関数 \(g\) 上で最小値をとる \(x_0\) を求める。(\(h(1) = h(N)\) の場合や \(h(1)\) が極値の場合は特殊ケースとして処理)
\(\displaystyle g(x) = \min\left(h(x), \frac{h(N) - h(1)}{N-1}(x-1) + h(1)\right)\)
2回目の三分探索では 区間 \([x_0+1, x_0+N-1]\) 上の関数 \(h\) で \(x_1\) を求める。
この求めた \(x_0\) と \(x_1\) について、点 \(w\) と \(x_0\) を通る直線 と 点 \(w\) と \(x_1\) を通る直線が 点 \(w\) から 凸多角形 \(P\) への接線になる。
計算量
\(O(\log N)\)
実装
凸多角形の頂点は反時計回りのリストとして持つとする。
from math import atan2
def binary_search(f, L):
left = 0; right = L
while left+1 < right:
mid = (left + right) >> 1
if f(mid) < 0:
left = mid
else:
right = mid
return right
def convex_polygon_tangent(p0, qs):
L = len(qs)
d = L//3
gx = (qs[0][0] + qs[d][0] + qs[2*d][0]) / 3
gy = (qs[0][1] + qs[d][1] + qs[2*d][1]) / 3
x0, y0 = p0
dx = gx - x0; dy = gy - y0
sv = - x0*dy + y0*dx; cv = - x0*dx - y0*dy
h = lambda p: atan2(p[0]*dy - p[1]*dx + sv, p[0]*dx + p[1]*dy + cv)
i0 = i1 = -1
EPS = 1e-12
v0 = h(qs[0])
v1 = h(qs[L-1])
if abs(v0 - v1) < EPS:
v2 = h(qs[1])
# assert v2 != v0
if v0 < v2:
i0 = L-1
else:
i1 = L-1
else:
v2 = h(qs[1])
if v2 >= v0 < v1:
i0 = 0
elif v2 <= v0 > v1:
i1 = 0
else:
g = lambda x: min((v1 - v0)*x/(L-1) + v0, h(qs[x]))
i0 = binary_search(lambda x: g(x+1) - g(x), L-1)
if i1 == -1:
B = i0-L
k = binary_search(lambda x: h(qs[x+B]) - h(qs[x+B+1]), L)
i1 = (i0 + k) % L
else:
B = i1-L
k = binary_search(lambda x: h(qs[x+B+1]) - h(qs[x+B]), L)
i0 = (i1 + k) % L
# assert i0 != -1 != i1
return i0, i1
参考
-
Chazelle, Bernard, and David P. Dobkin. "Intersection of convex objects in two and three dimensions." Journal of the ACM (JACM) 34.1 (1987): 1-27.