円への接視線ベクトル

メモ。やっぱ数式書けるのは便利!
C=(C_x, C_y)を視点から見た円の中心の位置ベクトル。V=(V_x, V_y)を視線ベクトル(単位ベクトル)とする。

接視線ベクトルは、視点から接点までの距離h = V\cdot C = V_xC_x + V_yC_yとすると  h Vとなる。
ここで円の中心から接点へのベクトルの大きさ||h V - C||^2 = r^2が成り立つはずなので、これを元に解いていく。
簡単のためC_x = 0とするとh = V_yC_y
||h V - C||^2 = r^2
(hV_x)^2 + (hV_y - C_y)^2 = r^2
h^2(V_x^2+V_y^2) - 2hV_yC_y + C_y^2 = r^2
Vは単位ベクトル。あとh = V_yC_yから
h^2 - 2h^2 + C_y^2 = r^2
-h^2 = r^2 - C_y^2
V_y^2C_y^2 = C_y^2 - r^2
V_y^2 = 1 - \frac{r^2}{C_y^2}
V_x^2 = 1 - V_y^2
わあなんと簡単になってしまったんだ。とかいう話なんだけど。迂遠なことやってる。算数チックに簡単に解けるんだろうなぁ。
かなり語弊があるけど、つくづく(最先端を除く)数学ってのはバカ向けナイフだと思う。
追記:
算数というか相似の考え方で一発ぢゃん、ということに気がついて激しく落ち込んだ。
斜辺cy / 高さ r = 単位ベクトルなので斜辺 = 1 / 求める V_x だ。
やっぱり僕馬鹿過ぎる。
ついでにテストしてみたコードを貼っておく。

import Tkinter
import sys
import math

## frame size 
w,h = 450,450

axis_x = w/2
axis_y = h*80/100   # 80% of whole frame

## circle pos & radius
cy = 270
r = 90

## util funcs
def rel(x,y):
    return (axis_x+x, axis_y-y)
def circle(self, r, x,y):
    self.create_oval(x-r, y-r, x+r, y+r)

## make window & canvas
tk = Tkinter.Tk()
c = Tkinter.Canvas()
c.configure(width=w, height=h)

### make basic frame
# circle
c.circle = circle
c.circle(c, r, *rel(0, cy))
itemxaxis= c.create_line(0, axis_y, w, axis_y)
c.itemconfig(itemxaxis, fill="blue", arrow="last")
itemyaxis= c.create_line(axis_x, 0, axis_x, h)
c.itemconfig(itemyaxis, fill="blue", arrow="first")

## calc cross-section
conn_l = conn_r = rad_l = rad_r = None
def cross(event):
    global c, conn_l, conn_r, rad_r, rad_l, cy

    for item in (conn_l, conn_r, rad_l, rad_r):
	c.delete(item)

    py = float(axis_y)-event.y
    ry = cy - py
    
    r_sq = r*r
    ry_sq = ry*ry
    h = math.sqrt(ry_sq - r_sq)
    vy = math.sqrt(1.0 - r_sq/ry_sq)
    vx = math.sqrt(1.0 - vy*vy)
    print vx, vy

    dstx_l, dsty_l = rel(h*(-vx),  h*(vy)+py)
    dstx_r, dsty_r = rel(h*(vx),  h*(vy)+py)
    centerx, centery = rel(0, cy)
    clkx, clky = rel(0, py)

    conn_l = c.create_line(clkx, clky, dstx_l, dsty_l)
    c.itemconfig(conn_l, fill="red")
    conn_r = c.create_line(clkx, clky, dstx_r, dsty_r)
    c.itemconfig(conn_r, fill="red")
    rad_l = c.create_line(centerx, centery, dstx_l, dsty_l)
    c.itemconfig(rad_l, fill="darkgreen", arrow="last")
    rad_r = c.create_line(centerx, centery, dstx_r, dsty_r)
    c.itemconfig(rad_r, fill="darkgreen", arrow="last")
##
c.bind('<Button1-Motion>', cross)
c.bind('<ButtonPress-1>', cross)

c.pack()

tk.mainloop()

追記のとおりなので

***************
*** 43,53 ****
      py = float(axis_y)-event.y
      ry = cy - py
      
!     r_sq = r*r
!     ry_sq = ry*ry
!     h = math.sqrt(ry_sq - r_sq)
!     vy = math.sqrt(1.0 - r_sq/ry_sq)
!     vx = math.sqrt(1.0 - vy*vy)
      print vx, vy
 
      dstx_l, dsty_l = rel(h*(-vx),  h*(vy)+py)
--- 43,51 ----
      py = float(axis_y)-event.y
      ry = cy - py
      
!     vx = r/ry
!     vy = math.sqrt(1-vx*vx)
!     h  = vy*ry
      print vx, vy
  
      dstx_l, dsty_l = rel(h*(-vx),  h*(vy)+py)
***************

ぐぬぬぬ。