AutoCAD の LISP で、オブジェクトの交点を求める

今日は今までと趣向を変えて、AutoCAD というか、AutoLISP のネタです。
# AutoLISP でオブジェクトの交点を求める方法
## 昔ながらのやり方 (2次元限定)
直線の交点は、`(inters ～)`で求められます。
円弧は円の一部、ふくらみを持ったポリラインも直線と円弧の組み合わせと考えれば、円と直線、円と円の交点が求められれば、スプライン以外の交点が求められそうです。
円と円の交点を求めるルーチンは作ったことはないですが、円と直線の交点を求めるルーチンは作成したことがあります。
```lisp:① 直線と円の交点
;;;***************************************************
;;;  円と直線の交点を求める
;;;   直線の方程式：ax+by+c = 0
;;;   円の方程式：(x-xp)^2 * (y-yp)^2 = r^2
;;;***************************************************
(defun line_circle_cross_point_sub (
  a b c xp yp r
  /
  d e f
  )

  (setq
    d (+ (* a xp) (* b yp) c)
    e (+ (expt a 2.0) (expt b 2.0))
    f (sqrt (- (* e (expt r 2.0)) (expt d 2.0)))
  );setq
  (if (equal (* e (expt r 2.0)) (expt d 2.0) (margin_distance))
    (list
      (list
        (+ xp (/ (* -1.0 a d) e));x
        (+ yp (/ (* -1.0 b d) e));y
      );list
    );list
    (list
      (list
        (+ xp (/ (+ (* -1.0 a d) (* b f)) e));x1
        (+ yp (/ (- (* -1.0 b d) (* a f)) e));y1
      );list
      (list
        (+ xp (/ (- (* -1.0 a d) (* b f)) e));x2
        (+ yp (/ (+ (* -1.0 b d) (* a f)) e));y2
      );list
    );list
  );if

);defun
```
```lisp:直線の構成点(x1,y1)と(x2,y2)から、直線の方程式を求める (① を計算するのにあると便利)
;;;***************************************************
;;;  直線の構成点(x1,y1)と(x2,y2)から、直線の方程式
;;;  「ax+by+c = 0」の a,b,c を求める
;;;***************************************************
(defun xy1_xy2_to_abc (
  xy1 xy2
  /
  x1 x2 y1 y2
  )

  (setq
    x1 (car xy1)
    y1 (cadr xy1)
    x2 (car xy2)
    y2 (cadr xy2)
  );setq
  (list
    (- y2 y1)
    (- x1 x2)
    (- (* y2 (- x2 x1)) (* x2 (- y2 y1)))
  );list

);defun
```
```lisp:許容マージン (① に必要、この値以下なら、0とみなされる)
(defun margin_distance() (eval 0.0000000001))
```
## VLAオブジェクトを使用したやり方
VLA オブジェクトって、なんだかよく分からなかったので、今まであまり使っていなかったんですが、別件でExcelのデータを AutoLISP で扱う事があり、その際に
「なにやら`vlax-invoke-method`を使って、色々出来そうらしい」
という事を体験して、ちょっと習得してみようと思った次第。
```lisp:オブジェクトの交点を求める：VLAオブジェクト版
;;;***************************************************
;;;  オブジェクトの交点を求める
;;;  引数 obj_1 と obj_2 はそれぞれ交点を求めるオブジェクトの図形名
;;;***************************************************
(defun get_inters_point (
  obj_1 obj_2
  /
  vobj_1 vobj_2
  intPoints
  tempPoint
  r_list
  )

  (setq r_list nil)
  (if
    (and obj_1 obj_2
      (setq vobj_1 (vlax-ename->vla-object obj_1))  ;オブジェクト名を基にしてVLAオブジェクトを取得
      (setq vobj_2 (vlax-ename->vla-object obj_2))  ;オブジェクト名を基にしてVLAオブジェクトを取得
      (setq intPoints (vla-IntersectWith vobj_1 vobj_2 acExtendNone))  ;交点を取得(オブジェクトの延長はしない)
    );and
    (if (/= (type intPoints) vlax-vbEmpty)  ;交点は有効？
      (progn
        (setq tempPoint (vlax-safearray->list (vlax-variant-value intPoints)))  ;交点リストを取得
        (while tempPoint
          (setq
            r_list (append r_list (list (list (car tempPoint) (cadr tempPoint) (caddr tempPoint))))
            tempPoint (cdddr tempPoint)
          );setq
        );while
      );progn
    );if
  );if
  (progn r_list)

);defun
```
ObjectARX や VBA の intersectWith メソッドと同じです(というかそれを呼び出していると思われます)。
`acExtendNone`の部分は、オブジェクトが実際に交差している交点のみ求める指定です。
ここを変更すると、下記の様になります。
* `acExtendThisEntity`：1つ目の引数(obj_1)のオブジェクトは延長、二つ目の引数(obj_2)のオブジェクトは延長しない場合の交点を求める
* `acExtendOtherEntity`：1つ目の引数(obj_1)のオブジェクトは延長せず、二つ目の引数(obj_2)のオブジェクトは延長する場合の交点を求める
* `acExtendBoth`：1つ目の引数(obj_1)のオブジェクトと二つ目の引数(obj_2)のオブジェクトの双方を延長する場合の交点を求める

という風になります。
今は改善されてる(と思いたい)のですが、<font color="red">***古いバージョンの AutoCAD では、intersectWith に精度上の問題がある事が指摘されており、使用に対しては十分検証する事***</font>をお勧めいたします。
# やっぱりLISPだね
AutoCADでちょっとしたカスタマイズや自動作図は、AutoLISPがやっぱり便利ですね。