AutoCAD명령내리기 acedCommand() , acedCmd() 이해와 사용법

이번 강좌는 AutoCAD 명령을 내리기 위한 방법을 알아봅시다.

리습에서 프로그램을 작성하다보면 명령어로 많은 작업을 하게 됩니다. 이때 많이 사용되는 함수가 (command)함수이죠.
바로 이 명령과 동일한 역할을 하는 함수가 ObjectARX에서 acedCommand()와 acedCmd()임니다.
만약 가지고 있는 리습을 Arx로 바꾸고자 하신다면 이 명령들을 잘 아셔야 겠죠.^.^;

=============================================================================================
1. acedCommand()

int acedCommand( int rtype, …);
이 함수는 가변인자를 가지는데 AutoCAD는 인자를 쌍으로 취급함으로써 명령을 수행합니다. 쌍중 첫번째는 다음에 올 인자의 타입을 표시하고, 두번째가 실제데이타를 포함하고 있습니다. 리스트의 마지막은 하나의 인자로 0 또는 RTNONE이 오는데 명령리스트 끝을 의미합니다..

일반적으로 첫번째 파라미터는 RTSTR(String을 말함)이라는 result type codes가 오고 두번째 데이터 파라미터는 실행할 명령 문자열이 오는 경우가 많은데 꼭 그런건 아니고 명령을 문자열로 전달할 경우만 해당됩니다. 보통 변수에 담아서 명령을 실행하는 경우는 해당되는 타입을 써주어야 겠죠.
각 타입은 ADSCODES.H에 정의되어 있는 result type codes들중의 하나입니다.

아래 원을 그리는 명령을 보시면 이해가 잘 되실겁니다.
acedCommand(RTSTR, “circle”, // AutoCAD Command
RTSTR, “5, 5, 0”, // center point
RTSTR, “2,24086”, radius
RTNONE // end of list
);

다른 예를 하나 볼까요.
int docmd()
{
ads_point p1;
ads_real rad;

if( acedCommand(RTSTR, “circle”, RTSTR, “0,0”, RTSTR, “3,3”, 0) != RTNORM)
retrun BAD;

if( acedCommand(RTSTR, “setvar”, RTSTR, “thickness”, RTSHORT, 1, 0) != RTNORM)
retrun BAD;

p1[X] = 1.0; p1[Y] = 1.0; p1[Z] = 3.0;
rad = 4.5;
if( acedCommand(RTSTR, “circle”, RT3DPOINT, p1, rad, 0) != RTNORM)
retrun BAD;

return GOOD;
}
같은 circle 명령이라도 문자열로 점을 전달할수도 있고 정수로도 전달할수 있습니다.

여기서 RTSTR은 뒤에 나오는 실제데이터 유형과 값을 일치하는 Result Type code를 사용해야 합니다. 이것때문에 리습에서 사용하는 (command)루틴과 인자가 틀리게 되므로 주의하셔야 됩니다.

————————————————
참고 Result Type Codes
————————————————
RTNONE No result value
RTREAL Real (floating-point) value
RTPOINT 2D point (X and Y; Z == 0.0)
RTSHORT Short (16-bit) integer
RTANG Angle
RTSTR String
RTENAME Entity name
RTPICKS Selection set name
RTORINT Orientation
RT3DPOINT 3D point (X, Y, and Z)
RTLONG Long (32-bit) integer
RTVOID void (blank) symbol
RTLB List begin (for nested list)
RTLE List end (for nested list)
RTDOTE Dot (for dotted pair)
RTT AutoLISP t (true)
RTNIL AutoLISP nil
RTDXF0 Group code zero for DXF lists(used only with aculBulidList())
——————————————————————————-

=============================================================================================
2.acedCmd() 함수

acedCmd()함수는 acedCommand() 함수와 동등하지만 결과 버터 리스트 형태로 AutoCAD에 값을 전달합니다.
이것은 acedCommand()명령과 비교해서 명령리스트를 구성하는데 복잡한 논리가 필요한 경우에 유용합니다.

원형은 int acedCmd(struct resbuf *rbp); 입니다.
인자에 주목해 주세요. 왠 구조체 포인터가 인자로 들어있네요. 이 resbuf(Result buffer)가 어디서 부터 오냐하면 acutBildList()함수에서 미리 명령리스트를 구성한것을 받아 오는 것입니다. 이렇게 함으로써 명령리스트가 컴파일시 고정되지 않고 런타임시 수정될 수 있는 장점을 가지고 있습니다. 이걸 위해서 이렇게 하는 거죠^.^;
하지만 단점은 실행시 약간 acedCommand() 명령보다 길어집니다.

이 명령을 이해하기 위해서 좀 복잡한 이야기를 하겠습니다. 이 명령이 어떻게 동작하는지를 이해하려면
resbuf구조체와 acutBuildList()함수를 드리고 어떻게 사용하는지 살펴보죠.

==============
resbuf 구조체
===============
result buffer는 직역하면 결과버퍼인데 엔티티나 테이블을 억세스하거나 iteration 또는 acedCommand(), acedCmd(), acutBuildList()등에 사용된다.
쉽게 설명하면 여러분이 AutoCAD에서 마우스로 엔티티들을 선택하였다고 하자.
화면에서는 여러분이 선택한 엔티티들이 점선으로 변화지만 AutoCAD에서는 내부적으로 이것들을 링크드리스트(linked list) 자료구조를 형성하여 담아둡니다.

struct resbuf
{
struct resbuf * rbnext; /* Linked list pointer */
short restype;
union ads_u_val resval;

}
첫번째 rbnext는 연결된 resbuf 구조체를 가리키고 두번째 restype은 실제 저장될 resval 데이타 종류가 어떠한 것인지를 가르키죠..

실제 저장되는 ads_u_val 정의는 아래를 참고하세요.

union ads_u_val
{
ads_real rreal;
ads_real rpoint[3];
short rint;
char * rstring
long rlname;
long rlong;
struct ads_binary rbinary;
}

갑자기 많은 이야기를 해서 복잡해 졌는데 정리하면 위에서 복잡한 논리적 명령리스트라고 구성하는데 이것들을 사용한다고 했는데 대표적인 경우가 사용자가 어떤 대상들을 선택하여 원하는 작업을 수행하고자 할때 resbuf라는 링크드리스트 자료구조를 이용해서 엔티티의 선택같은 논리적 명령리스트를 작성할 수 있다는 이야기 입니다.

그럼 result buffer lists를 사용해서 어떻게 엔티티를 선택하는지 알아보죠.

리습을 사용해본적인 있다면 Entity를 선택하는 명령인 Entsel을 알것이다.
ADS에서도 acedEntSel() 함수를 사용해서 엔티티를 선택할수 있다. 그리고 엔티티의 근원적인 데이타를 얻어내기 위해서 acdbEntGet()

을 사용한다. 물론 Lisp명령으로는 EntGet()이 있다.
이와 같이 거의 대부분의 Lisp명령어와 ARX명령어는 클래스 소속+ 리습함수로 이로어진 경우가 많으니 알아두시길.
여기서 중요한 것은 acdbEntGet() 함수가 리턴값으로 취하는 것이 연결리스트 구조인 rebuf 라는 점이다.

다음은 result buffer에 “Circle” 엔티티 정보가 들어가 있는 예입니다.

——— ——— ———
result ——-> ———-> ———-> ———->
——— ——— ———
-1 0 8
——— ——— ———
ename “circle” “0”
——— ——— ———

——— ——— ———
———-> ———-> ———->
——— ——— ———
10 40 210
——— ——— ———
5.0 0.0
5.0 2.24086 0.0
0.0 1.0
——— ——— ———

DXF code를 이해하고 있다면 그림을 좀더 쉽게 알아볼수 있는데 중간값은 DXF 그룹코드로써
-1은 도면 요소의 이름, 0은 도면 요소 유형 8은 레이어, 10은 1차 점; 선 또는 문자 도면요소, 원의 중심 등의 시작점등을 나타냅니다.

자 그러면 result buffers 포인터 두개를 만들어서 어떻게 사용하는지 보죠.

resbuf *rb1, *tb1;

그리고 acedEntSel()과 acdbEntGet()함수를 사용한후 *rb1이 result buffer을 가졌다면.

tb1 = rb1; tb1은 연결리스트에서 처음을 가르킨다.
이제 *tb1을 이동시키면서 연결리스트를 조회할 수 있는데 다음으로 이동하기 위해서는

tb1 = tb1->rbnext;

우리가 rad변수에 위 그림에 저장된 원의 반경을 담아내려면. 원의 반경 DXF 코드가 40이므로 아래와 같이 할수 있습니다.

ads_real rad;
.
.
.
if (tb->restype == 40)
{
// We have the radius field of the entity
rad = rb1->resval.rreal;
}

ObjectARX에서는 result buffers를 사용하는 방법을 알아보았다. 그러나 이를 너무 깊게 알려고 할 필요는 없다.
이는 ADS 시절에 주로 쓰이는 방법이고 현재 ObjectARX에서는 엔티티 접근과 변경 생성, 심볼테이블접근등에 더 나은 메커니즘을 제공 합니다.
하지만 여전히 ObjectARX에서 엔티티와 심볼, 그리고 위에 살펴본 acedCommand(), acedCmd(), acutBuildList()에 사용되므로 아셔야 겠죠.^.^;

이제 다시 acedCmd()로 돌아가서. acedCmd() 명령을 수행하기 위해서 acutBuildList()함수에서 result buffers 링트드리스트를 먼저 만들어야 한다고 했죠. 그럼 acutBuildList() 함수를 살펴봅시다.

struct resbuf *acutBuildList(int rtype, …) ;

함수수행의 결과로 rsbuf* 를 리턴하고 있다. 그런데 만약 이 함수가 실패한다면 무엇을 리턴할까…
답은 NULL

다음은 acedCmd() 명령을 이용해서 현재 AutoCAD 그리픽 화면에 REDRAW 명령을 수행하는 예입니다.

struct resbuf *cmdlist;
cmdlist = acutBuildList(RTSTR, “redraw”, 0);

if (cmdlist == NULL)
{
acdbFail(“Could not create list\n”);
return BAD;
}

acedCmd(cmdlist);
acutRelRb(cmdlist);

또 아래 예제는 라인을 생성하는 예이다.

resbuf *rb;
// struct resbuf *rb; // if you prefer

if(( rb = acutBuildList(RTSTR, “line”,
RTSTR, “1,1”,
RTSTR, “2,2”,
RTSTR, “”, // ENTER Key
RTNONE)) != NULL)

{
if(acedCmd(rb) = RTNORM)
{
//acedCmd worked
}
else
{
// acedCmd failed
}
else
{
// acedBuildList failed
}
if (rb != NULL)
{
acutRebRb(rb);
}

}

참고로 위에 예제들을 보면 마지막에 acutRelRb() 함수가 보일것이다 이 함수는 result buffer나 링크드리스트 result buffer 메모리를 해제시켜는 기능을 합니다.

자 이제 acedCommand()함수와 acedCmd()함수에 대한 설명을 마칩니다. 잘 이해가 갔는지 모르겠네요.
눈으로만 으로 이해했다고 하지말고 AutoCAD 어떤 명령이라도 한번 위에 명령들을 활용해서 구현해 보시길. 백문이 불여일타라고 100번 보는것 보다 한번 고민하면서 작성해보는게 공부가 됩니다…

그럼 이번 강좌는 여기서 줄이겠습니다. ^.^;;

vs2008 에서 fatal error C1083: 포함 파일을 열 수 없습니다.

[#M_ more.. | less.. | ……………………………..
…………………………
2>컴파일하고 있습니다…
1>컴파일하고 있습니다…
2>cl : 명령줄 warning D9035 : ‘Wp64’ 옵션은 더 이상 사용되지 않으므로 이후 릴리스에서 제거될
예정입니다.
2>StdAfx.cpp
1>cl : 명령줄 warning D9035 : ‘Wp64’ 옵션은 더 이상 사용되지 않으므로 이후 릴리스에서 제거될
예정입니다.
1>StdAfx.cpp
1>c:\objectarx\objectarx 2010\inc\acarray.h(93) : fatal error C1083:
포함 파일을 열 수 없습니다. ‘type_traits’: No such file or directory
1>빌드 로그가 “file://d:\prg\cmshop18\CmShopApp\Win32\Debug\BuildLog.htm”에
저장되었습니다.
1>CmShopApp_AC – 오류: 1개, 경고: 1개
2>c:\objectarx\objectarx 2010\inc\acarray.h(93) : fatal error C1083:
포함 파일을 열 수 없습니다. ‘type_traits’: No such file or directory
2>빌드 로그가
“file://d:\prg\cmshop18\CmShopObject\Win32\Debug\BuildLog.htm”에 저장되었습니다.
2>CmShopObject – 오류: 1개, 경고: 1개
========== 빌드: 성공 0, 실패 2, 최신 0, 생략 0 ==========
_M#]VS2008 을 새롭게 설치하고 라이브러리도 잘 연결하고 컴파일을했더니 위와 같이 나왔다..

참으로 난감한 상황이다… 위에러의 해결책은 의외로 간단하다..

테크니칼 리포트를 설치하면 된다고 나와있는데.. 서비스팩1을 설치하면 말끔하게 처리 된다.

다들 위의 상황으로 난감해 하지 마시길 ,,,

어떤 점에 있는 라인의 ID 획득하기

AcDbObjectId FindLine(AcGePoint3d pt)
{
    AcDbObjectId yLineId;
    ads_point ptUnder = {pt.x, pt.y, pt.z};
    ads_name ss;
    int res;

    resbuf* rb=acutBuildList(RTDXF0, _T(“LINE”), 0);        //필터

    double BoxScale=0.0;
    BoxScale = Utils::getPickBox();        //pickbox 크기

    AcGePoint3d p1, p2;
    p1.x = pt.x – BoxScale*0.5;
    p1.y = pt.y – BoxScale*0.5;
    p2.x = pt.x + BoxScale*0.5;
    p2.y = pt.y + BoxScale*0.5;

    if (RTNORM == (acedSSGet(_T(“C”),&p1,&p2 , rb, ss)))
    {
        AcDbObjectIdArray ids;
        Utils::ss2Array(ss, ids);

        while(ids.length()){
            AcDbObjectPointer<AcDbLine>  pLine(ids.first(), AcDb::kForRead);
            ids.removeFirst();

            if(pLine.openStatus()== Acad::eOk){
                if(pLine->colorIndex() == 2)
                {
                    yLineId = pLine->objectId();

                }
                pLine->close();
            }

        }//end-while
    }//end-ssget

    return yLineId;
}

어떤 좌표에 위치한 엔터티 구하기


int ArxGetEntUnderPos(AcDbObjectIdArray& ids , const AcGePoint3d& pt)


{


                  ads_point ptUnder = {pt.x, pt.y, pt.z};


 


                  ads_name ss;


                  int res;


                  if (RTNORM != (res = acedSSGet(“:E”, ptUnder, NULL, NULL, ss)))


                  {


                                    // There is probably nothing under the cursor,


                                    // so return and let AutoCAD process the message


                                    return RTFAIL;


                  }


                 


                  long length = 0L;


                  acedSSLength(ss, &length);


                  if (0 == length)


                  {


                                    // There is nothing under the cursor,


                                    // so there is no need to show the context menu.


                                    // Let AutoCAD process the message.


                                    acedSSFree(ss);


                                    return RTFAIL;


                  }


 


                  ads_name ename;


                  for(int i = 0;i < length;i++)


                  {


                                    acedSSName(ss, i, ename);


                                    AcDbObjectId entId;


                                    if(Acad::eOk != acdbGetObjectId(entId, ename)) continue;


                                    ids.append(entId);


                  }


                  acedSSFree(ss);


 


                  return RTNORM;


}


acedSSGet 함수

도면에서 여러개의 엔티티를 선택할때 acedSSGet 함수를 사용합니다.


 

int 
acedSSGet (
    const char *str, 
    const void *pt1,
    const void *pt2,
    const struct resbuf *entmask, 
    ads_name ss);
 
 
첫번째 인수로 특정 알파벳을 넣으면 여러가지 옵션으로 엔티티들을 가져올수 있습니다.
 
그런데 첫번째 인수로 “C”, “CP”, “F”, “W”, “WP” 등 범위를 지정하는 옵션을 줄 경우
주의할 점이 있습니다.
지정한 범위가 AutoCAD 화면상에 보여야 합니다. 그렇지 않으면 엔티티를 가져오지 못합니다.
(이것두 첨엔 왜그런지 몰라서 한참을 고민했었습니다.. ㅠㅠ)
 
그래서 전 acedSSGet 함수를 사용할때 범위를 지정하는 옵션을 줄 경우
zoom을 extend로 해놓고 사용합니다.^^

[출처] acedSSGet 함수..|작성자 뚜기