点から円への接点を求める

## 円に対する点の位置と接線の関係
-  点が円の外側にあるとき　･･･　接線は２本
-  点が円周上にあるとき　　･･･　接線は１本
-  点が円の内部にあるとき　･･･　接線は存在しない

<img width="650" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/29744d83-dd8b-7cd4-767f-f1582af6678e.png">

## 円の接線の方程式
円 $x^2+y^2=r^2$ の円周上の１点 $P(x_0,y_0)$ を通る接線の方程式を求める。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/b9279e9a-b3be-241c-b2d3-14d1fcc4eb89.png">
$P$ を通る円の接線は半径 $OP$ に垂直。
$OP$ の傾きは $\dfrac{y_0}{x_0}$ なので、接線の傾きを $m$ とすると、
$$\dfrac{y_0}{x_0}m=-1\ \ \ \ \ \ \therefore\ m=-\frac{x_0}{y_0}$$
となる。
したがって、この接線は点 $(x_0,y_0)$ を通る傾きが $-\dfrac{x_0}{y_0}$の直線で、その方程式は、
$$y-y_0=-\dfrac{x_0}{y_0}(x-x_0)$$
です。（[直線の方程式](https://qiita.com/tydesign/items/4d3909a826363d8fc29f) 参照）
この式の両辺に $y_0$ を掛けると、
$$y_0(y-y_0)=-\frac{x_0(x-x_0)y_0}{y_0}$$
$$y_0y-y_0^2=-x_0x+x_0^2$$
となり、$-x_0x$ を左辺に $-y_0^2$ を右辺に移項すると、
$$x_0x+y_0y=x_0^2+y_0^2$$
また、点 $P(x_0,y_0)$ は円 $x^2+y^2=r^2$ の円周上の点なので、
$$x_0^2+y_0^2=r^2$$
が成り立つ。したがって、接線の方程式は
$$x_0x+y_0y=r^2$$
となる。
また、中心の座標が $(a,b)$ 半径が $r$ の円 $(x-a)^2+(y-b)^2=r^2$ おいて、円周上の点$P(x_0,y_0)$ を通る接線の方程式は、
$$(x_0-a)(x-a)+(y_0-b)(y-b)=r^2$$
です。

### 【例】中心(3,2) 半径 5 の円の円周上の点 (6,6) を通る接線の方程式を求める
接線の方程式 $(x_0-a)(x-a)+(y_0-b)(y-b)=r^2$ より、
$(6-3)(x-3)+(6-2)(y-2)=5^2\  \dashrightarrow \ 6\cdot(x-3)+4\cdot(y-2)=25\  \dashrightarrow \ 6x-18+4y-8-25=0$
よって接線の方程式は、
$$6x+4y-42=0$$

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/e6df1fd9-3617-ebb2-0e55-4f809fc578eb.png">

## 極線の方程式
円 $x^2+y^2=r^2$ と円外の点 $Q(x_1,y_1)$ について、点 $Q$ から円には2つの接線が引ける。
その接線の接点をそれぞれ $A,B$ としたときの直線 $AB$ の方程式を求める。
この直線 $AB$ を円の**極線**、点 $Q$ のことを円の**極**という。 

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/2dedcbe7-4e08-f3d1-d6b6-5505f8cb1ebb.png">

点 $A$ の座標を $(x_a,y_a)$、点 $B$ の座標を $(x_b,y_b)$ をしたとき、
点 $A,B$ を通る円の接線の方程式はそれぞれ、
$$x_ax+y_ay=r^2$$
$$x_bx+y_by=r^2$$
となる。この接線が 点 $Q(x_1,y_1)$ を通るので、
$$x_ax_1+y_ay_1=r^2 \ \cdots \ ①$$
$$x_bx_1+y_by_1=r^2 \ \cdots \ ②$$
となる。この2つの式をよく見ると、

*  ①は、式 $x_1x+y_1y=r^2$ の $x,y$ に点$A$ の座標 $(x_a,y_a)$ を代入した式
*  ②は、式 $x_1x+y_1y=r^2$ の $x,y$ に点$B$ の座標 $(x_b,y_b)$ を代入した式

になっている。
異なる2点を通る直線は一意に定まるので、この2つの式①②が成り立つをいうことは、
直線 $AB$ の方程式が $x_1x+y_1y=r^2$ であるということを示している。
よって直線 $AB$ の方程式は、
$$x_1x+y_1y=r^2$$
これが円 $x^2+y^2=r^2$ と円外の点 $Q(x_1,y_1)$ に関する極線の方程式です。

また、中心が $(a,b)$ 半径が $r$ の円 $(x-a)^2+(y-b)^2=r^2$ とその円外の点 $(x_1,y_1)$ に関する極線の方程式は、
中心が $(0,0)$ の円の場合より、$x$ 軸方向に $-a$、$y$ 軸方向に $-b$ 平行移動した円と考えられるので、
その方程式は、
$$(x_1-a)(x-a)+(y_1-b)(y-b)=r^2$$
となる。

## 点から円への接点を求める
中心が $(x_0,y_0)$ 半径が $r$ の円 $(x-x_0)^2+(y-y_0)^2=r^2$ とその円外の点 $Q(x_1,y_1)$ の接線の2つの接点 $A,B$ の座標を求める。

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/1f6cbfef-239d-9525-6cd8-1c199f9852ae.png">

#### 求め方
円と円外の点 $Q(x_1,y_1)$ に関する極線の方程式を求めて、その極線と円の交点を、[円と直線の交点を求める](https://qiita.com/tydesign/items/36b42465b3f5086bd0c5)公式を使って求める。

極線 $AB$ の方程式は、
$$(x_1-x_0)(x-x_0)+(y_1-y_0)(y-y_0)=r^2$$
この式を展開して、
$x_1x-x_1x_0-x_0x+x_0^2+y_1y-y_1y_0-y_0y+y_0^2-r^2=0$
整理すると、
$$(x_1-x_0)x+(y_1-y_0)y+(x_0^2+y_0^2-x_1x_0-y_1y_0-r^2)=0 \ \cdots \ ①$$
この式は、[直線の方程式](https://qiita.com/tydesign/items/4d3909a826363d8fc29f)の一般形 $ax+by+c=0$ の係数$a,b,c$をそれぞれ、

* $a=(x_1-x_0)$
* $b=(y_1-y_0)$
* $c=(x_0^2+y_0^2-x_1x_0-y_1y_0-r^2)$

とした形になっている。

中心の座標が $(x_0,y_0)$ 半径 $r$ の円 $(x-x_0)^2+(y-y_0)^2=r^2$ と直線 $ax+by+c=0$ の交点を求める公式は、交点を $A,B$ とした場合、（[円と直線の交点を求める](https://qiita.com/tydesign/items/36b42465b3f5086bd0c5) 参照）
$$A=\left(\frac{aD-b\sqrt{(a^2+b^2)r^2-D^2}}{a^2+b^2}+x_0,\frac{bD+a\sqrt{(a^2+b^2)r^2-D^2}}{a^2+b^2}+y_0\right) \ \cdots \ ②$$
$$B=\left(\frac{aD+b\sqrt{(a^2+b^2)r^2-D^2}}{a^2+b^2}+x_0,\frac{bD-a\sqrt{(a^2+b^2)r^2-D^2}}{a^2+b^2}+y_0\right) \ \cdots \ ③$$
$$ただし、D=\mid ax_0+by_0+c\mid$$
で、この $D$ の式に、① の係数 $a,b,c$ にあたる値を代入すると、
$D=\mid ax_0+by_0+c\mid=\mid (x_1-x_0)x_0+(y_1-y_0)y_0+(x_0^2+y_0^2-x_1x_0-y_1y_0-r^2)\mid$
$=\mid x_1x_0-x_0^2+y_1y_0-y_0^2+x_0^2+y_0^2-x_1x_0-y_1y_0-r^2\mid=\mid -r^2 \mid=r^2$
つまり、
$$D=r^2$$
これを②③に代入すると、
$$A=\left(\frac{ar^2-b\sqrt{(a^2+b^2)r^2-(r^2)^2}}{a^2+b^2}+x_0,\frac{br^2+a\sqrt{(a^2+b^2)r^2-(r^2)^2}}{a^2+b^2}+y_0\right)$$
$$=\left(r\cdot\frac{ar-b\sqrt{a^2+b^2-r^2}}{a^2+b^2}+x_0,r\cdot\frac{br+a\sqrt{a^2+b^2-r^2}}{a^2+b^2}+y_0\right) \ \cdots \ ④$$
$$B=\left(\frac{ar^2+b\sqrt{(a^2+b^2)r^2-(r^2)^2}}{a^2+b^2}+x_0,\frac{br^2-a\sqrt{(a^2+b^2)r^2-(r^2)^2}}{a^2+b^2}+y_0\right)$$
$$=\left(r\cdot\frac{ar+b\sqrt{a^2+b^2-r^2}}{a^2+b^2}+x_0,r\cdot\frac{br-a\sqrt{a^2+b^2-r^2}}{a^2+b^2}+y_0\right) \ \cdots \ ⑤$$
式④⑤の $a,b$ に、① の係数 $a,b$ にあたる値を代入すると、
$$A=\left(r\cdot\frac{(x_1-x_0)r-(y_1-y_0)\sqrt{(x_1-x_0)^2+(y_1-y_0)^2-r^2}}{(x_1-x_0)^2+(y_1-y_0)^2}+x_0,
r\cdot\frac{(y_1-y_0)r+(x_1-x_0)\sqrt{(x_1-x_0)^2+(y_1-y_0)^2-r^2}}{(x_1-x_0)^2+(y_1-y_0)^2}+y_0\right)$$
$$B=\left(r\cdot\frac{(x_1-x_0)r+(y_1-y_0)\sqrt{(x_1-x_0)^2+(y_1-y_0)^2-r^2}}{(x_1-x_0)^2+(y_1-y_0)^2}+x_0,r\cdot\frac{(y_1-y_0)r-(x_1-x_0)\sqrt{(x_1-x_0)^2+(y_1-y_0)^2-r^2}}{(x_1-x_0)^2+(y_1-y_0)^2}+y_0\right)$$
となる。
これが、円 $(x-x_0)^2+(y-y_0)^2=r^2$ とその円外の点 $Q(x_1,y_1)$ の接線の2つの接点 $A,B$ の座標を求める公式です。

#### 【例】中心(3,2) 半径 5 の円に対して、点 (15,5) から引いた２つの接線の接点を求める。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/24264eae-931c-f606-201a-60405f65c593.png">
上記公式より、
$x_1-x_0=15-3=12\ \ \ \,\ \ \ \ y_1-y_0=5-2=3$
点 $A$ の $x$ 座標 $x_a$ は、
$$x_a=5\cdot\frac{12\cdot5-3\sqrt{12^2+3^2-5^2}}{12^2+3^2}+3=\frac{253-40\sqrt{2}}{51}\fallingdotseq 3.851597$$
点 $A$ の $y$ 座標 $y_a$ は、
$$y_a=5\cdot\frac{3\cdot5+12\sqrt{12^2+3^2-5^2}}{12^2+3^2}+2=\frac{127+160\sqrt{2}}{51}\fallingdotseq 6.926945$$
点 $B$ の $x$ 座標 $x_b$ は、
$$x_b=5\cdot\frac{12\cdot 5+3\sqrt{12^2+3^2-5^2}}{12^2+3^2}+3=\frac{253+40\sqrt{2}}{51}\fallingdotseq 6.069971$$
点 $B$ の $y$ 座標 $y_b$ は、
$$y_b=5\cdot\frac{3\cdot 5-12\sqrt{12^2+3^2-5^2}}{12^2+3^2}+2=\frac{127-160\sqrt{2}}{51}\fallingdotseq -1.946552$$
よって、接点 $A,B$ の座標は、
$$A=\left(\frac{253-40\sqrt{2}}{51},\frac{127+160\sqrt{2}}{51}\right)\fallingdotseq (3.851597,6.926945)$$
$$B=\left(\frac{253+40\sqrt{2}}{51},\frac{127-160\sqrt{2}}{51}\right)\fallingdotseq (6.069971,-1.946552)$$
となる。

## サンプルプログラム
#### 円図形を選択して、その次に点を指示。その点から選択した円への2つの接線を作成して、その接点の座標を出力する

```scheme
;; 点から円への接線を作成して、その接点の座標を出力する
(defun c:CreateCircleTangent (/ circle point n)
    ;; 円を選択
    (while (null circle)
        (setvar 'ERRNO 0)
        (setq circle (car (entsel "円を選択: ")))
        (cond
            ;; 空振り
            (   (= 7 (getvar 'ERRNO))
                (princ "\n**空振り!!再選択**\n")
                (setq circle nil)
            )
            ;; 空Enter
            (   (= 52 (getvar 'ERRNO))
                (princ "\n**空Enter!!再選択**\n")
                (setq circle nil)
            )
            ;; 図形が選択された
            (circle
                (if (/= "CIRCLE" (entity:GetType circle))
                    (progn
                        (princ "\n**円ではありません!!再選択**\n")
                        (setq circle nil)
                    )
                    (princ "\n**円が選択されました。**\n")
                )
            )
        )
    )
    ;; 点を選択
    (while (null point)
        (setq point (getpoint "点を指示: " ))
        (if point
            (if (circle:IsOnOrContains circle point)
                (progn
                    (princ "\n**円の内部の点です!!再選択**\n")
                    (setq point nil)
                )
	        (princ "\n**点が指示されました。**\n")
            )
        )
    )
    (setq n 1)
    (mapcar
        (function
            (lambda (tp)
                (princ (strcat "\n接点の座標" (itoa (setq n (1+ n))) "："))
                (entmake:2PointsXline point (princ tp)) ;_構築線を作成
            )
        )
        (circle:GetTangentPoints circle point) ;_接点を計算
    )
    (princ)
)

;; == sub functions ==
;; 図形のDXF定義データの値を取得
(defun entity:GetDxf (g e)
    (cond
        ((= (type e) 'ENAME) (cdr (assoc g (entget e))))
        ((listp e) (cdr (assoc g e)))
    )
)
;; 図形のタイプを取得
(defun entity:GetType (e) (entity:GetDxf 0 e))
;; 円の中心を取得
(defun circle:GetCenterPoint (e) (entity:GetDxf 10 e))
;; 円の半径を取得
(defun circle:GetRadius (e) (entity:GetDxf 40 e))
;; 円周上の点か調べる
(defun circle:IsOnBase (cx cy cr x y)
    (equal (+ (expt (- x cx) 2) (expt (- y cy) 2)) (* cr cr) 1e-8)
)
(defun circle:IsOn (circle point / r o)
    (setq r (circle:GetRadius circle) o (circle:GetCenterPoint circle))
    (circle:IsOnBase (car o) (cadr o) r (car point) (cadr point))
)
;; 円の内部の点か調べる
(defun circle:ContainsBase (cx cy cr x y)
    (< (+ (expt (- x cx) 2) (expt (- y cy) 2)) (* cr cr))
)
(defun circle:Contains (circle point / r o)
    (setq r (circle:GetRadius circle) o (circle:GetCenterPoint circle))
    (circle:ContainsBase (car o) (cadr o) r (car point) (cadr point))
)
;; 円の内部の点か調べる 円周上の点も含める
(defun circle:IsOnOrContains (circle point)
    (or (circle:IsOn circle point) (circle:Contains circle point))
)
;; 円の接点を求める
(defun circle:GetTangentPoints (circle point / a b c d r o)
    (setq r (circle:GetRadius circle)
          o (circle:GetCenterPoint circle)
          a (- (car point) (car o))
          b (- (cadr point) (cadr o))
          c (+ (* a a) (* b b))
          d (sqrt (- c (* r r)))
    )
    (list
        (list
            (+ (/ (* r(- (* a r) (* b d))) c) (car o))
            (+ (/ (* r(+ (* b r) (* a d))) c) (cadr o))
        )
        (list
            (+ (/ (* r(+ (* a r) (* b d))) c) (car o))
            (+ (/ (* r(- (* b r) (* a d))) c) (cadr o))
        )
    )
)
;; 構築線の基本DXF定義
(defun entmake:XlineBase (p u)
    (list '(000 . "XLINE")
          '(100 . "AcDbEntity")
          '(100 . "AcDbXline")
          (cons 010 p)
          (cons 011 u)
    )
)
;; 基点と単位方向ベクトルから構築線を作成
(defun entmake:Xline (p u) (entmake (entmake:XlineBase p u)) (entlast))
;; 2点を結ぶベクトルの成分を計算
(defun vector:From2Points (p q) (mapcar '- q p))
;; ベクトル から 単位ベクトルを計算
(defun vector:ToUnitVector (v)
    (   (lambda (n)
            (if (equal 0.0 n 1e-10)
                nil
                (mapcar '/ v (list n n n))
            )
        )
        (distance '(0. 0. 0.) v)
    )
)
;; 2点から単位ベクトルを計算
(defun vector:2PointsToUnitVector (p q) (vector:ToUnitVector (vector:From2Points p q)))
;; 2点指示で構築線を作成
(defun entmake:2PointsXline (a b)
    (entmake:Xline a (vector:2PointsToUnitVector a b))
)
```

## 点から円への接線を作図する
中心が $(x_0,y_0)$ 半径が $r$ の円 $(x-x_0)^2+(y-y_0)^2=r^2$ とその円外の点 $Q(x_1,y_1)$ の2つの接線を作図する。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/384860/e7e4d1b0-82bc-a9e8-7b53-a4955e935303.png">

1. 点 $C$ 、点 $Q$ の中点をとる
1. その中点を中心とした、線分$CQ$を直径とする円を作図する
1. その作図した円と円 $(x-x_0)^2+(y-y_0)^2=r^2$ の2つの交点 $A,B$と点 $Q(x_1,y_1)$を通る直線をそれぞれ作図する
2. その2つの直線が点から円への接線（半円の弧に対する円周角は直角）