==================================================発行部数997======
     AutoCADカスタマイズ入門講座              No.26 1999/11/23
===================================================================
 発行が遅れてしまい申し訳ございませんでした。
では、早速先週の続きをはじめたいと思います。

--------------------------------------------------------------------
 1.2直線の情報を比較する2
--------------------------------------------------------------------
 さて、前回は求めた2直線の作図角度を、場合によっては補正しなければ
ならないというところまで説明しました。次に説明しなければならない事は
、以前説明したように実数(xxx.xxxの様に小数点が付く数)を直接比較する
ようなプログラムを書くべきではないという事です。繰り返しになりますが
、これまで説明してきたように2つの直線の角度を求め、それらの作図角度
が等しい場合に何らかの処理を行うプログラムを作る場合、例えばそれぞれ
の直線の作図角度をang1,ang2とすれば、

(if (= ang1 ang2)
   (progn
   ...
   処理
   ...
    )
)

等とする事は避けなければなりません。自分では十分理解しているつもりで
もついこの様に書いてしまいがちです。気を付けましょう。
では、どの様にするかといえば、

(if ( < (abs (- ang1 ang2)) eps)
  ...
    処理
    ....

の様にします。これも以前説明しましたね。つまり、2つの角度の差を取り
その差がある値以下なら同じ角度とみなします。その差を変数epsに次の様
に設定しています。

(setq eps 1e-3)

1e-3等とeを付けて数を表す方法を指数表現などと呼びます。1e-3は0.001と
同じ事ですので、(setq eps 0.001)と書いても何ら問題はありません。
即ち、今回のプログラムでは2つの直線の角度の差が0.001以下であれば同一
の角度とみなしています。

--------------------------------------------------------------------
 2.2直線の情報を比較する3
--------------------------------------------------------------------
 2直線の角度が同じであれば次の処理に移ります。2直線が同一の角度であ
っても、直線間の距離が遠ければ重なっている事にはなならい事はすぐ分か
ると思います。これも以前説明しましたね。ですので、ここでは2直線間の
距離を調べます。また非常に簡単な(というか、泥臭い)手を使いますが、
2直線間の距離を調べるもっと素敵な方法をご存知の方がいらっしゃいまし
たら、ぜひその方法をご教授下さい。という訳で、まず最初に2つの直線に
対して直行する(仮想の)直線を求めます。

(setq p30 (polar p10 (+ ang1 (/ pi 2.0)) 1.0))

といっても簡単で、直線1の始点p10から直線1の作図角度+(π/2)の角度で距
離が1.0の位置にある点を求めています。(polar)関数の書式は
(polar 基準点 角度 距離)で基準点から、ある角度で、ある距離だけ離れた
点を返します。また、直線1に直行する直線を求めれば、既に同一角度であ
る事が保証されている直線2に対しても直行している事になります。

次に今求めた点p10,p30で表される直線と2つの直線の交点を求めます。直線
の交点を求めるには(inters)関数を用います。(inters)関数の書式は、
(inters Line1の始点 Line1の終点 Line2の始点 Line2の終点 flag)で、交
点が存在すればその交点を、存在しなければnilを返します。flagにnilを指
定するとLine1、Line2は無限の長さの直線とみなされます。また、nil以外
の値を指定すると有限長の直線であるとみなされます。(inters)関数で3次
元空間上の直線を考慮する場合にはもっと注意しなければならない点がある
のですが、今回は2次元図面を想定するのでここでは説明しません。興味の
ある方はマニュアルなどをご覧ください。

プログラムでは次の様にして、直線1,2とそれらに直行する直線の交点を求
めています。flagにnilが指定してあるのでそれぞれの直線は無限の長さと
仮定され必ず交点が求まります。

(setq intp1 (inters p10 p11 p10 p30 nil))
(setq intp2 (inters p20 p21 p10 p30 nil))

次に求めた2つの交点intp1、intp2間の距離を求めます。2点間の距離を求め
るには(distance)関数を用います。書式は(distance 点1 点2)です。角度を
比較した時と同様に、仮に2つの直線がぴったり重なっていたとしても何ら
かの原因で(distance)関数が0を返さない事は十分考えられますので、やは
り以下の様に誤差を考えてプログラムを書きます。

(if (< (distance intp1 intp2) eps)
      (progn
    ...
   ...

前回のプログラムはここまででした。今回は直線が本当に重なっているか(
一方の直線の始点、終点の少なくとも一方が、もう一方の直線上に乗ってい
るかどうか)の判定をするプログラムを追加します。

--------------------------------------------------------------------
 3.点が直線上にあるかどうかの判定
--------------------------------------------------------------------
 次に直線が本当に重なっているかどうかの判定を行います。ここまで(前
回と今回をあわせ)説明してきた条件を2つの直線がすべてクリアしていれば
、2つの直線の作図角度は同一であり、また2つの直線は1直線上に存在しま
す。しかしこれだけでの条件では前回説明したように、破線はすべて1つの
直線となってしまいます(注:ただしAutoCADのLineコマンドを用いて書いた
破線等はデータ上では一つの直線なのでこの例には当てはまりません)。

 そこで、y=ax という直線の方程式を用いて一方の直線が本当にもう一方
の直線上にあるかどうかを確かめます。この式のaを直線の傾きといいます
。例えば、2点A(0,0)とB(4,2)を通る直線の傾きaは、

y = (2-0) ÷ (4-0) で求まります。
つまり、
y= (Bのy座標 - Aのy座標) ÷ (Bのx座標 - Aのx座標)です。

結局この直線の方程式は、
y = 0.5x となります。

さて、ここでこの直線は2点A,Bとは別に(2,1)という点も通ります(簡単なグ
ラフを書くとすぐ分かると思います)。この点のx,y座標をそれぞれ、y=0.5x
に代入するとどうなるでしょうか?。そう、左辺と右辺が等しくなります。
これらか分かるように、ある直線上の点をその直線を表す方程式に代入する
と左辺と右辺は等しくなります。

上記で説明した部分を今回プログラムに新しく追加しました。
プログラムではここで説明した事をそのまま行い、まず、直線1の始点終点
が直線2上にあるか?、次に直線2の始点終点が直線1上にあるかどうかを確
かめています。詳しくはプログラムの方をご覧ください。

--------------------------------------------------------------------
 4.merge.lsp(改定版)
--------------------------------------------------------------------
 最後に今回修正したプログラムを添付します。次回は2つの直線を1つに置
き換える部分を考えていきたいと思います。
また、merge.lspは殆どテストをしていないので不具合があると思います。
不具合がありましたらお知らせ頂けたら幸いです。
それでは、次号をお楽しみに。

;↓ ここから
(defun C:merge()
  ; 2つのエンティティを選択
  (setq name1 (car (entsel "最初の直線を選択して下さい:")))
  (setq name2 (car (entsel "2本目の直線を選択して下さい:")))

  ; 2直線の始点、終点を得る
  (setq p10 (cdr (getprop name1 10)))
  (setq p11 (cdr (getprop name1 11)))
  (setq p20 (cdr (getprop name2 10)))
  (setq p21 (cdr (getprop name2 11)))

  ; 2直線の作図角度を得る
  (setq ang1 (angle p10 p11))
  (setq ang2 (angle p20 p21))

  ; 求めた2直線の作図角度を補正する
  ; 作図角度 < π
  (setq ang1 (mangle ang1))
  (setq ang2 (mangle ang2))

  (setq eps 1e-3)   ; 許容値
  
  ; 作図角度の判定
  (if ( < (abs (- ang1 ang2)) eps)
    (progn
    (print "作図角度は同一です")
    ; 2直線に直行する直線の準備
    ; 始点p10、終点p30の直線を設定する
    (setq p30 (polar p10 (+ ang1 (/ pi 2.0)) 1.0))

    ; 2直線と垂線の交点を求める
    (setq intp1 (inters p10 p11 p10 p30 nil))
    (setq intp2 (inters p20 p21 p10 p30 nil))

; ↓ ここから今回付け加えた部分
    ; 交点間の距離を求め、許容値以下か判定
    (if (< (distance intp1 intp2) eps)
      (progn
        (print "直線間の距離は許容値以下です")
        ;条件を抜けるかどうかのフラグを設定
        (setq mflag 0)
        ;2直線の傾きを求める
        (setq katamuki1 (/ (- (cadr p11) (cadr p10))
                       (- (car p11) (car p10))))
        (setq katamuki2 (/ (- (cadr p21) (cadr p20))
                       (- (car p21) (car p20))))

        ; 直線1の始点が直線2上にあるかどうか確かめる
        (setq uhen (* katamuki2 (car p10)))
        (setq sahen (cadr p10))
        (if (< (abs (- uhen sahen)) eps)
          (setq mflag 1)
        )
        ; 直線1の終点が直線2上にあるかどうか確かめる
        (if (= mflag 0)
          (progn
            (setq uhen (* katamuki2 (car p11)))
            (setq sahen (cadr p11))
            (if (< (abs (- uhen sahen)) eps)
               (setq mflag 2)
            )
          )
        )
        ; 直線2の始点が直線1上にあるかどうか確かめる
        (if (= mflag 0)
          (progn
            (setq uhen (* katamuki1 (car p20)))
            (setq sahen (cadr p20))
            (if (< (abs (- uhen sahen)) eps)
               (setq mflag 3)
            )
          )
        )
        ; 直線2の終点が直線1上にあるかどうか確かめる
        (if (= mflag 0)
          (progn
            (setq uhen (* katamuki1 (car p21)))
            (setq sahen (cadr p21))
            (if (< (abs (- uhen sahen)) eps)
               (setq mflag 4)
            )
          )
        )
        (if (> mflag 0)
           (progn
              (print "2直線は重なっています")
              (terpri)
           )
        )
        )
      )
    )
  )
)

;---------------------------
; 属性取得関数
;---------------------------
(defun getprop(entname grpcode / elist)
  (setq elist (entget entname))
  (assoc grpcode elist)
)

;------------------------------------------
; π以上の角度をπ以下の角度に補正する関数
;------------------------------------------
(defun mangle(angle / i)
  (setq i 0)
  (while (= i 0)
    (if (> angle pi)
      (setq angle (- angle pi))
      (setq i 1)
    )
  )
  (setq angle angle)
)
↑ ここまで

●バックナンバーは下記のURLで参照する事が出来ます。
http://www2u.biglobe.ne.jp/~Saturn5/alisp.htm
====================================================================
■登録/解除の方法
http://www2u.biglobe.ne.jp/~Saturn5/alisp.htm
「AutoCADカスタマイズ入門講座」は、上記URLよりいつでも
登録/解除可能です。
====================================================================
●広告の問い合わせ
広告のお問い合わせは以下のメールアドレスへお願いします。
wankichi@mba.nifty.ne.jp
====================================================================
■「AutoCADカスタマイズ入門講座」No.26
発行責任者 :わんきち(wankichi@mba.nifty.ne.jp)
発行システム:インターネットの本屋さん『まぐまぐ』
              http://www.mag2.com/
              マガジンID:0000011579
====================================================================