AutoLISP 강좌 [2]


1. 입문(I) - AutoLISP의 기본 개념
2. 입문(II) - 사용자 정의 함수 제작
3. 입문(III) - 그 밖의 기본 기능들
4. 응용(I) - Entity와 Selection-Set
5. 응용(II) - Symbol table과 Device 제어
6. 응용(III) - Dialogue box 제어
7. 활용예제


AutoLISP을 통해 프로그래밍이라는 새로운 세계에 발을 들여 놓게 되는 CAD 사용자들을 위해 간단히 프로그래밍에 관해 이야기할까 한다. 프로그래밍은 어떤 작업을 처리하기 위한 각각의 사고과정을 코드화하는 작업이라 생각한다. 따라서, 프로그래밍의 결과인 프로그램 자체의 수행결과도 중요하겠지만 프로그래밍 과정인 소스 코드(source code)도 무척 중요하다. 소스 코드는 프로그래머의 사고과정을 여실히 나타내게 되는데 코드가 두서(?)가 없이 혼란스러운 경우에는 그 프로그램이 잘 동작하더라도 그 수행 결과에 대해 의문을 가지지 않을 수 없다. 그렇다고 해서 잘 작성된 프로그램이 반드시 잘 작동하는 것은 아니다. 그러나, 그렇게 작성된 프로그램은 나중에 보더라도 낯설지 않고 다른 사람이 보더라도 수정이 용이한 이점을 가질 수 있다. 한마디로 말해서 유지보수가 편하다고 할 수 있다. 그리고, 무엇보다 강조하고 싶은 점은 자신이 작성한 프로그램은 최소한 자신은 알아볼 수 있을 정도로 나름대로의 일관성과 자연스러운 구조를 기반으로 작성하기 바란다는 것이다. 그것이 처음부터 이루어지지는 않겠지만 처음에 습관을 잘 들이는 것이 중요하다 하겠다.

2. 사용자 정의 함수 제작

2.1 AutoLISP 프로그램 작성 단계

이제 본격적으로 AutoLISP 그래밍을 시작하기 전에 간단히 AutoLISP 프로그램 작성 단계에 대해 알아보자.

AutoLISP은 잘 아는 바 대로 인터프리터 방식의 언어이다. 이런 류의 언어의 장점으로 사용자가 대화식으로 프로그래밍을 작성할 수 있어 편하다는 얘기는 많이 들었으리라 믿는다. 그럼, 과연 대화식으로 프로그래밍하는 것은 어떤 것을 의미하는 것일까? 직접해 보면 금방 실감할 수 있으리라 생각된다. 우리가 어떤 AutoCAD 상의 작업을 AutoLISP을 이용해서 프로그래밍하고 싶을 때면 우선 AutoCAD에서 하는 작업의 단계를 정리해본다. 그런 다음 각각의 작업을 AutoCAD 상태에서 AutoLISP 식을 입력해 가면서 한 문장씩 확인한다. 마지막으로 그 내용을 하나의 함수 내지는 상황에 따라 여러개의 함수로 나누어 코딩하기만 하면 프로그램이 완성된다. 다음에 그 과정을 정리해 보았다.

1) 캐드상의 작업 과정을 나열한다.
2) 프로그래밍을 위해 필요한 정보를 수집한다.
3) 어떤 AutoLISP 함수가 사용될지를 시험해 본다.
4) 프로그램을 코딩한다.
5) 실행시켜 본다.
6) 디버깅한다.

그러나, 같은 내용을 ADS로 제작하고자 한다면 간단한 기능의 시험을 위해서도 수 분의 컴파일 작업을 해야만 하기 때문에 효율이 상당히 떨어진다. 반면, 최종 프로그램의 실행 속도면에서는 컴파일된 ADS 프로그램이 월등히 빠르기 때문에 - 특히 반복적인 계산 작업 - 일단 AutoLISP을 사용하여 자신이 생각한 작업 단계나 알고리즘(algorithm)의 검증을 수행한 후 ADS를 사용하여 코딩하는 것도 한 방법이 되겠다.

앞으로의 강좌에서 사용될 예제들은 위의 과정을 통해 제작과정을 설명하도록 하겠다. 항상 위의 과정과 일치하지는 않겠지만 거의 대부분의 경우에 위의 과정을 따르게 될 것이다.

2.2 다중선 그리기

AutoLISP을 공부하면서 한번정도씩은 만들어 보았으리라 생각되는 예제이다. 두점을 지정하면 지정한 간격으로 지정한 회수만큼 반복하여 선을 그려주는 프로그램이 되겠다. 완성된 프로그램은 리스트 1에 있지만 우선 각자 나름대로 위에서 언급한 프로그램 작성 단계를 따라서 프로그램을 작성해 보기 바란다.

1) 캐드상에서의 작업은 단순히 선을 그리는 명령을 지정한 회수만큼 반복하면 된다.
2) 프로그램에 입력될 정보로는 시작점과 끝점이 필요하다.

선간의 간격과 반복 회수는 미리 정의된 것으로 한다. 즉, 전역변수로 설정한다.

우선, 예제 프로그램을 작성하기 위해 필요한 개념들에 대해 설명할까 한다.

2.3 함수 정의

AutoLISP에도 다른 프로그래밍 언어들과 같이 다양한 기능의 함수들이 있으며 또한 사용자가 새로운 함수를 정의할 수도 있다. 이처럼 새로운 함수를 정의할 때 사용되는 함수가 DEFUN(define function) 함수이며 사용법은 다음과 같다.

(defun <함수명> <변수 목록> <함수 내용> ...)

1) 함수명

함수명은 프로그램의 다른 부분에서 그 함수를 호출할 때 사용되는 이름이며 영문자나 숫자 뿐만 아니라 ^, !와 같은 기호도 사용가능하다.

이번 예제에서는 리스트 1에 보이는 draw-oline, draw-mline, C:MLINE이 함수명이다.

2) 변수 목록

변수 목록은 함수를 호출할 때 함수로 전달되는 인수들의 목록을 뜻한다. 이 목록에는 추가적으로 함수내에서만 사용되는 변수(지역 변수)들의 목록이 /로 구분되어 포함될 수도 있다. 이때 인수들의 목록과 지역 변수들의 목록을 구분짓는 /의 앞뒤로 최소한 한칸의 공백이 있어야 한다. 이렇게 해서 다음과 같은 형태의 변수 목록이 있을 수 있겠다.

(a b) ; a, b라는 인수가 있는 경우

(/ c d) ; c, d라는 지역 변수가 있는 경우

(a b / c d) ; a, b라는 인수와 c, d라는 지역 변수가 있는 경우

() ; 인수나 지역 변수가 전혀 없는 경우

이번 예제에서 C:MLINE함수를 예로 들면 이 함수로 전달되는 인수는 없으며 함수내에 지역 변수 sp, ep 만이 있음을 알 수 있다.

그럼, 여기서 잠시 전역 변수(global variable)와 지역 변수(local variable)의 기능에 대해 알아보고 넘어가도록 하자. 전역 변수는 말 그대로 프로그램 전 범위내에서 통용되는 변수이며 지역 변수는 그 변수가 선언된 함수내에서만 통용되는 변수이다. 위에서 보았듯이 특별히 지역 변수로 선언되지 않은 모든 변수는 전역 변수가 된다. 그러나, 가급적 전역 변수의 사용을 줄이고 함수내에서 해결 할 수 있는 것은 함수내에서 지역 변수로 정의하여 사용하도록 하자. 그것이 메모리를 절약할 수 있는 방법도 된다. 왜냐하면 지역 변수의 경우에는 임시 변수로서 함수의 사용이 끝나면 그 정의가 사라지지만 전역 변수의 경우에는 선언되는 그 시점부터 특별한 일이 없는 한 계속 존재하게 된다. 그리고, 전역 변수의 경우에는 나름의 방식을 정해 선언하는 것이 좋겠다. 이 강좌에서는 변수명 앞뒤에 '*'를 붙여서 프로그램의 앞부분에서 초기화하여 전역 변수를 선언하도록 하겠다. 리스트 1의 앞부분에 나오는 *COPY*와 *OFF*가 바로 이런 식으로 선언된 전역 변수이다. 이런 방식의 장점은 프로그램 어디서 전역 변수가 사용되고 있는 지를 금방 파악할 수 있을 뿐만 아니라, 의미를 알 수 없는 변수가 있더라도 그것이 전역 변수이며 그 의미를 쉽게 파악할 수 있게 해준다.

3) 함수 내용

함수의 내용으로는 AutoLISP의 일반적인 식을 사용할 수 있으며 여기서 중요하게 기억하여야할 사항은 함수의 결과값은 함수 내용 중 마지막에 있는 식의 결과값이 된다는 사실이다. 이번 예제의 경우에는 단지 선을 그리는 것이 함수의 결과이므로 최종 결과값이 중요한 의미를 가지지 않지만 예를 들어 어떤 계산을 수행하는 함수라면 어떤 것이 그 함수의 결과값이 되는 지가 중요한 의미를 갖는다. 다음의 경우를 보자.

(defun tan (rad)
  (/ (sin rad) (cos rad))
)

(tan (/ PI 4.0)) ; 결과값은 1.0

함수의 수행 과정을 살펴보면 다음과 같다.

rad = PI / 4.0 = 0.785398

(/ (sin 0.785398) (cos 0.785398)) 결과값

(sin 0.785398) -> 0.707107

(cos 0.785398) -> 0.707107

(/ 0.707107 0.707107 ) -> 1.0 => 최종 결과값

위와 같이 마지막 계산의 결과가 반환되는 것을 알 수 있다. 그리고 이 기능을 이용하면 다음과 같은 것이 가능하겠다. 예를 들어 어떤 함수에서 그 함수를 호출한 함수에 어떤 지역 변수의 값을 전달하고자 하는데 그 변수의 계산식이 함수의 내용중 맨 마지막에 있지 않다면 어떻게 해야 할까? 그럴때는 함수의 마지막줄에 그 변수의 이름을 써 넣기만 하면 된다. 아래 내용을 유심히 보면서 잘 생각해 보기 바란다.

(defun space (num / str)
  (setq str "")
  (repeat num
    (setq str (strcat str " "))
  )
  str
)

물론, 이 경우에는 꼭 마지막에 STR 변수의 이름을 쓸 필요는 없다. 다만 보다 확실하게 수행되도록 하기 위해 그렇게 했을 뿐이다. 왜 꼭 쓰지 않아도 되는지도 생각해 보자.

이번 예제에는 프로그램의 마지막에 PRINC라는 함수가 있는데 이 함수의 일반적인 기능은 화면상에 문자열을 출력하는 것이지만 위와 같은 경우에는 프로그램의 종료후에 필요없는 함수의 결과값이 출력되지 않도록 하기 위해 사용하였다. 이 함수를 사용하지 않고 프로그램을 수행시켜 보고 차이를 확인해 보기 바란다.

4) 함수의 재귀적 호출

함수내에서는 외부의 다른 함수를 호출할 수 있다. 뿐만 아니라, 자기 자신을 호출할 수도 있다. 이런 호출 방식을 함수의 재귀적 호출(recursive call)이라고 한다. 다음의 예를 보자. 임의의 수의 계승(factorial)을 구하는 함수를 함수의 재귀적 호출을 사용하여 제작해 보았는데 함수명으로 수학에서 계승 기호로 사용되는 !를 사용하였다.

(defun ! (n)
  (if (zerop n)
    (quote 1)
    (* (float n) (! (1- n)))
  )
)

이런 방식을 응용하여 제작한 예를 이번 예제에서도 볼 수 있다. 리스트 1의 draw-mline 함수를 참고하기 바라며, 함수를 재귀적으로 호출할 때는 함수의 탈출 조건을 명확히 하여야 한다는 사실에 주목하자. 위의 예와 draw-mline 함수의 if문의 조건과 함수가 재귀적으로 호출되는 부분을 잘 보기 바란다.

그리고, 마지막으로 함수를 만드는 원칙에 대해 몇가지 설명하고자 한다. 프로그램이 간단하다면 굳이 한 프로그램을 여러개의 함수로 나눌 필요는 없을 것이다. 그러나 프로그램이 길어져서 다단계의 작업을 하게 되는 경우와 어떤 작업을 반복해서 여러번 수행하게 되는 경우에는 각각의 작업 내용을 하나의 독립된 함수로 만들어서 호출하여 사용하는 것이 바람직하겠다. 가끔 보면 몇 백라인이 넘는 프로그램이 하나의 함수로 되어 있는 프로그램이 있는데 그런 것은 가급적 피하는 것이 프로그램 작성자나 나중에 그 코드를 보게되는 다른 사람을 위해 좋겠다. 참고로 한 함수의 길이는 50라인 이내가 적당하다. 그것은 주 원인이 PC의 화면 한계(25라인) 때문이긴하지만 에디터상에서 편집을 위해 페이지 넘김키를 위, 아래로 한번 정도 움직임으로써 함수 전체 내용을 파악할 수 있을만큼 충분히 작아야 함을 뜻한다. 그러나, 꼭 이러한 원칙을 따른 필요는 없겠고 자신이 판단해서 함수가 좀 길다고 판단되면 다시 한번 함수의 내용을 잘 살펴 본 후 잘게 나누어 주는 것이 좋으리라 생각된다. LISP 언어는 굉장히 모듈화 하기 쉽게 잘 정의되어 있으며 프로그램의 정확한 파악, 운용 및 관리를 위해 모듈화 하는 것이 바람직하다. 뿐만 아니라, 모듈화된 코드는 코드 재활용(?)을 할 수 있어 매우 편리하다.

2.4 AutoCAD 용 명령어 제작

앞서 설명한 DEFUN 함수를 사용하면 AutoCAD 용 명령어도 제작할 수 있습니다. 제작 방법은 함수명 앞에 C:를 붙이기만 하면 된다. 바로 이번 예제에도 이 경우에 해당되는 C:MLINE 함수가 있으며 MLINE이라는 명령을 사용하여 함수를 호출할 수 있다. 물론, 일반적인 함수 호출 방법을 사용하여 (C:MLINE)으로 할 수도 있다. 실제로 AutoLISP 프로그램내에서 다른 AutoLISP으로 정의된 명령어를 사용할 때 종종 이 방법을 사용한다.

만약 AutoCAD 내에 이미 존재하는 명령어를 재정의하고자 한다면 UNDEFINE 명령을 사용하여 미리 그 명령을 사용할 수 없게 하여야만 재정의된 명령을 사용할 수 있다. 그리고 원래의 AutoCAD 명령을 임시로 사용하고자 한다면 명령어 앞에 .을 붙여서 사용하면 된다. 다시 원래의 AutoCAD 명령으로 환원하고자 한다면 REDEFINE 명령을 사용하면 된다.

Command: UNDEFINE
Command name: QUIT
Command: QUIT
Unknown command. Type ? for list of
commands.
Command: .QUIT
Really want to discard all changes to
drawing? N
Command: REDEFINE
Command name: QUIT
Command: QUIT
Really want to discard all changes to
drawing?

2.5 사용자의 입력

사용자로부터 입력을 받기 위한 함수들의 사용법을 아래에 나열해 보았다. '점'은 rubber-band line의 시작점을 뜻하며 다른 함수의 경우에는 생략 가능(시작점도 선택할 수 있음)하나 GETCORNER 함수의 경우에는 꼭 필요하다. '문자열'은 입력시에 사용자에게 묻게 될 질문의 내용이 되겠다.

1) 상대 각도 : (getangle [점] [문자열])
2) 대각선 점 : (getcorner <점> [문자열])
3) 거리 : (getdist [점] [문자열])
4) 정수 값 : (getint [문자열])
5) 키워드 : (getkword [문자열])
6) 절대 각도 : (getorient [점] [문자열])
7) 점 : (getpoint [점] [문자열])
8) 실수 값 : (getreal [문자열])
9) 문자열 : (getstring [CR모드] [문자열])

각도의 단위는 라디안(radian)이며 점은 3D 좌표 (3개의 실수의 리스트)이다. 그리고 되도록이면 GETREAL 함수보다는 GETDIST 함수를 사용하기를 권한다. 왜냐하면, GETDIST 함수를 사용하면 단순히 값을 입력하는 대신 두점을 선택하여 그에 상응하는 거리의 값으로 입력을 할 수도 있기 때문이다. 실제로 이런 방식이 AutoCAD 명령 사용중에도 많이 보이고 있다. 되도록이면 입력함수의 사용이나 질문의 내용에 있어서 AutoCAD 자체 명령어 사용에서와 비슷한 환경을 만들어 주기 바란다. 그래야만 사용하는 사용자가 특별한 훈련없이도 AutoCAD를 사용하던 방식대로 프로그램을 사용할 수 있기 때문이다. 다음의 몇가지 AutoCAD 명령어를 통해 예를 들어 보겠다.

Command: ZOOM
All/ ... /Window/<Scale(X/XP)>: W <= GETKWORD
First corner: <= GETPOINT
Other corner: <= GETCORNER
Command: INSERT
Block name(or ?): <= GETSTRING
Insertion point: <= GETPOINT
X scale factor<1>/Corner/XYZ: <=GETDIST
Y scale factor(default=X): <= GETDIST
Rotation Angle<0>: <= GETANGLE

또한, 사용자 입력 함수를 사용할 때 꼭 필요한 함수 하나가 있다. 그것은 INITGET 함수인데 이 함수는 사용자의 입력을 어떤 조건내에서 제한하여 입력의 오류를 방지하는 기능을 수행하게 된다. 아래에 있는 사용될 수 있는 제한 조건과 그 제한 조건을 사용가능한 사용자 입력 함수의 조합을 나타낸 표를 참고하기 바란다. (키워드의 기능에 대해서는 다음 기회에 설명하겠다.)

(initget [조건 값] [키워드])

조건 값 의 미
1 널(null) 입력 불가
2 영(zero) 입력 불가
4 음(minus) 입력 불가
8 도면 한계 검사 않음
16 사용되지 않음
32 rubber-band line이 점선으로 나타남
64 3D 좌표중 Z 좌표값을 무시함

함 수 1 2 4 8 32 64
GETINT * * *      
GETREAL * * *      
GETDIST * * *   * *
GETANGLE * *     *  
GETPOINT * *     *  
GETCORNER *     * *  
GETKWORD *     * *  
GETSTRING            

2.6 AutoCAD 명령어의 수행

COMMAND 함수는 AutoLISP 내에서 AutoCAD 명령어의 수행을 가능하게 하는 함수로 사용법은 다음과 같다.

(command <인수> ...)

이 함수는 보기보다 복잡한 기능을 가지고 있기 때문에 그 많은 기능을 한번에 모두 설명하기는 힘들고 앞으로도 굉장히 많이 다루어질 것이기 때문에 우선은 이 예제를 이해할 수 있는 정도의 설명에서 그칠까 한다.

COMMAND 함수에 전달되는 인수들이 모두 AutoCAD 명령어 해석기에 전달된다고 생각하면 간단하다. 따라서, 명령어나 그 명령어에서 사용되는 선택 사항이나 점 또는 어떤 수치들을 요구되는 순서와 형태에 따라 써 주기만 하면 된다. 명령어나 선택 사항은 문자열의 형태로 점은 리스트의 형태로 수치들은 정수나 실수의 형태로 써 주면 된다. 리스트 1의 draw-oline함수를 살펴 보자.

(command "LINE" -> 명령어 (문자열)

  (polar sp ang off) -> POLAR 함수의 결과값은 점(리스트)

  (polar ep ang off)

  "" -> 선택 사항 (문자열)

)

2.7 OFFSET된 선을 그리는 방법

그럼, 이제부터 위에서 배운 개념을 바탕으로 프로그램을 완성하기 위해 문제를 하나씩 해결해 나가자. 이번 예제에서 가장 많이 수행되는 작업은 지정된 두점을 지나는 선의 옵셋된 선을 그리는 기능이다. 이 기능은 주어진 회수만큼 반복될 것이므로 하나의 함수로 만드는 것이 편리하겠다. 우선 다음을 보자. 두점 p1, p2가 주어지고 옵셋 간격 off가 지정된 경우 옵셋된 선이 지나게 될 두점 p3, p4는 그림 1과 같이 정의된다.


그림 1. OFFSET 선의 정의

무척 복잡하다. 그러나, POLAR 함수를 사용하면 이것을 아주 쉽게 정의할 수 있다. 위와 같은 경우를 POLAR 함수를 사용하여 재정의하면 다음과 같다.

(setq p3 (polar p1 (+ ang (/ PI 2.0)) off))
(setq p4 (polar p2 (+ ang (/ PI 2.0)) off))

이제 이를 토대로 함수를 작성하여 보자. 완성된 예가 리스트 1의 draw-oline 함수이다. 수행되는지 한번 시험해 보자.

Command: (draw-oline (getpoint)
(getpoint) 1.0) <= 두점을 선택한다. (아무 메시지도 출력되지 않음)

지정한 두점을 지나는 선을 1.0만큼 옵셋한 선이 그려지는지 확인한다.

2.8 지정한 회수만큼 옵셋 실행

이 함수는 지정한 두점을 지나는 선을 OFF 값만큼 그리고 -OFF 값만큼 옵셋한다. 단, 지정 회수가 2회 이상이면 회수를 2씩 감소시키면서 그 과정을 반복한다. 여기서 앞서 언급한 함수의 재귀적 호출을 사용하여 간단하게 코딩하였다. 리스트 1의 draw-mline이 그것이며 수행이 가능한지 시험해 보자.

Command: (draw-mline (getpoint) (getpoint) 3 1.0) <= 두점을 선택한다.
(아무 메시지도 출력되지 않음)

여기서는 3개를 지정했지만 화면상에는 두개만 그려질 것이다. 이것은 함수를 재귀적으로 호출하였기 때문에 지정 회수가 홀수일 때에 중간선을 그려주는 기능은 이 함수를 호출하는 함수에서 처리하도록 하였다. 우선은 그렇게 알고 다른 회수를 지정해 보고 정상 작동하는 지를 확인하자. 항상 함수별로 동작을 확실하게 확인해 본 후에 다음으로 넘어가기 바란다. 특히, 함수내에 조건을 판단하는 문장이 있을 경우에는 그 조건식이 정상적으로 동작하는 지를 확인하기 위해 경계값을 입력해 보기 바란다. 위의 함수의 경우에는 지정 회수가 2미만이면 동작하지 않도록 되어 있으므로 1을 입력하였을때 실제로 동작을 하지 않는지 확인해 보자.

2.9 프로그램의 LOAD 및 실행

위의 내용을 모두 읽어 보셨다면 작성한 프로그램을 MLINE.LSP 또는 마음에 드는 이름으로 저장한다. 다만, 확장자는 LSP여야 함을 잊지 말자.

작성된 AutoLISP 프로그램을 LOAD하는 방법은 다음과 같다.

(load "프로그램명")

AutoCAD로 들어가서 LOAD 함수를 사용하여 프로그램을 로드한다.

Command: (load "MLINE")
C:MLINE

이제부터는 MLINE 명령을 AutoCAD의 다른 명령어들처럼 사용할 수 있다. C:MLINE 함수에서는 두점의 입력을 받고 입력된 두점과 전역 변수 *COPY*와 *OFF*를 이용하여 draw-mline을 수행한 후에 *COPY* 값, 즉 반복 회수가 홀수이면 입력된 두점을 지나는 중간선을 그리는 기능을 한다.

Command: MLINE
From point: <임의의 점을 선택한다.>
To point: <임의의 점을 선택한다.>

주어진 두점을 지나는 옵셋된 선이 양쪽으로 1.0만큼 떨어져서 그려지면 제대로 수행된 것이다. 이제 그외에 추가된 SETCOPY 명령과 SETOFF 명령을 사용하여 반복 회수와 간격을 바꾸어서 수행해 보자. 이 두 명령은 전역 변수로 설정된 *COPY*와 *OFF* 변수값을 변경하는 기능을 수행한다.


그림 2. 프로그램 수행 예



관련 자료
disk [1] MLINE.LSP : 다중선 그리는 프로그램

Last updated 2002-09-06 by choi@moon-sun.com
This page has been accessed : Counter times.
Home