개요
ObjectARX 응용 프로그램을 개발하다 보면 엔티티를 생성하는 것뿐만 아니라 생성된 엔티티를 선택하여 조회를 하기위한 정보로 사용하거나 어떤 조작을 해 주어야 하는 경우가 많이 발생한다. 셀렉션 셋은 생성된 엔티티 중에서 조회나 조작에 필요한 객체들만 선택적 세트로 만들어서 사용하고 싶을 때 사용되는 개념이다. 기존에 AutoLISP 과 ADS에서 사용하는 방식과 동일하다.
ObjectARX의 대부분 함수는 선택 세트와 엔티티를 처리할 때 해당 세트 또는 엔티티를 이름으로 식별한다. 이 이름은 오토캐드에서 할당하고 관리하는 long long 타입의 쌍이다. ObjecARX에서 선택 세트와 엔티티의 이름은 ads_name 형식을 갖는다.
//stdint.h
typedef long long int64_t;
//adsdef.h
typedef int64_t ads_name[2];
ObjectARX 응용 프로그램은 선택 세트 또는 엔티티를 조작하기 전에 라이브러리 함수 중 하나늘 호출하여 해당 세트 또는 엔티티의 현재 이름을 가져와야 한다. 이 함수는 선택 세트 또는 엔티티 이름을 반환한다.
int acedSSGet (
const char *str,
const void *pt1,
const void *pt2,
const struct resbuf *entmask,
ads_name ss);
선택 세트 및 엔티티 이름은 휘발성이다. 오토캐드에서 도면 작업을 하는 동안에만 적용되며, 오토캐드를 종료하거나 다른 도면으로 전환하면 손실된다.
현재 세션에만 적용되는 선택 세트의 경우 이름의 휘발성은 문제가 되지 않지만, 도면 데이터베이스에 저장되는 엔티티의 경우에는 문제가 된다.
Handling Selection Sets
ObjectARX에서 선택 집합을 처리하는 함수는 AutoLISP의 함수와 유사하다. acedSSGet() 함수는 선택 집합을 생성하는 가장 일반적인 방법을 제공한다. 이 함수는 다음과 같이 세 가지 방법 중의 하나로 선택 세트를 생성한다.
- 사용자에게 객체 선택을 요처하는 방법
- PICKFIRST 집합 또는 Crossing, Crossing Polygon, Fence, Last, Previous, Window, Window Polygon 옵션을 사용하여 선택할 객체를 명시적으로 지정하는 방법 또는 단일 점이나 점들의 펜스를 지정하는 방법
- 선택한 객체가 일치해야 하는 속성 및 조건 목록을 지정하여 현재 도면 데이터베이스를 필터링하는 방법
acedSSGet() 함수의 첫 번째 인수는 다음과 같이 요약된 표의 선택 옵션이 될 수 있다.
| Selection options for acedSSGet | 설명 |
| NULL | - pt1이 지정된 경우 : 단일 지점 선택 - pt1이 NULL인 경우 : 사용자 선택 |
| # | 비기하학적(전체, 마지막, 이전) ALL / LAST / PREVIOUS / GROUP 키워드를 추가하거나 제거하는 필터 제거 옵션 : "-#" 추가 옵션 : "+#" |
| :$ | 프롬프트 제공 |
| . | 사용자 선택 (단일 선택임) |
| :? | 사용자가 선택 중에 오토캐드가 이해하지 못하는 문자열을 입력하면 acedSSSetOtherCallbackPtr() 로 등록한 함수가 호출됨 |
| A | All |
| B | "W"와 "C"를 합쳐 놓은 개념 pt1, pt2 반드시 두 점 필요 |
| C | Crossing (걸치기만 해도 선택) pt1, pt2 반드시 두 점 필요 |
| CP | Crossing Polygon pt1은 점 리스트(resbuf 형태의 점 리스트), pt2는 NULL 이어야 함 |
| :D | Duplicates OK 기본적으로 acedSSGet으로 얻은 선택 집합은 같은 엔티티가 한 번만 들어감 하지만, ":D" 옵션은 같은 객체를 여러 번 선택하면 그 만큼 중복으로 들어감 |
| :E | Everything in aperture 화면에 보이는 pickbox 크기(커서 박스 크기) 기준으로 영역 안에 포함된 모든 객체가 한번에 선택됨 |
| F | Fence pt1은 점 리스트(resbuf 형태의 점 리스트), pt2는 NULL 이어야 함 여러 개의 점으로 이루어진 열린 폴리라인을 기준으로 그 선을 통과하는 객체들을 모두 선 |
| G | Group 사용자가 미리 정의된 Group 이름을 입력 받아 해당 Group에 포함된 객체들을 선택 세트로 반환 |
| I | Implied 사용자가 명령 실행 전에 미리 선택한 객체들을 가져온다 (오토캐드의 PICKFIRST 기능과 연결됨) 사용자 입력을 새로 받지 않고 기존 선택 상태를 그대로 가져옴 |
| :K | 키워드 콜백 (acedSSSetKwordCallbackPtr 함수 사용) |
| L | Last pt1, pt2는 반드시 NULL 이전 선택 세트를 선택 집합으로 반환 (가장 최근에 생성된 엔티티 1개만 선택됨) |
| M | Multiple 선택 모드가 아니라 키워드 필터 옵션 사용자가 여러 객체를 계속 선택하도록 허용 또는 반대로 한 번만 선택하도록 제한 ("+M", "-M") |
| P | Previous pt1, pt2는 반드시 NULL 마지막 생성 객체를 선택 집합으로 반환 (가장 최근에 생성된 엔티티 1개만 선택됨) |
| :S | Force single object selection only 오직 1개의 객체만 선택하도록 강제하는 옵션 ":S" 옵션을 사용할 경우 반드시 Multiple 키워드를 제거 해야 함 (":S-M") |
| W | Window (사각형 내부에 완전히 포함된 경우 선택) pt1, pt2 반드시 두 점 필요 |
| WP | Window Polygon pt1은 점 리스트(resbuf 형태의 점 리스트), pt2는 NULL 이어야 함 |
| X | Extended search (search whole databsae) 필터 조건에 맞는 모든 객체를 자동 선택 filter가 없으면 : 도면의 모든 객체 선택 filter가 있으면 : 조건에 만족하는 모든 객체 선 |
다음은 옵션 별 예시 코드이다.
ads_point pt1, pt2, pt3, pt4;
struct resbuf *pointlist;
ads_name ssname;
pt1[X] = pt1[Y] = pt1[Z] = 0.0;
pt2[X] = pt2[Y] = 5.0; pt2[Z] = 0.0;
// Get the current PICKFIRST set, if there is one;
// otherwise, ask the user for a general entity selection.
acedSSGet(NULL, NULL, NULL, NULL, ssname);
// Get the current PICKFIRST set, if there is one.
acedSSGet("I", NULL, NULL, NULL, ssname);
// Selects the most recently selected objects.
acedSSGet("P", NULL, NULL, NULL, ssname);
// Selects the last entity added to the database.
acedSSGet("L", NULL, NULL, NULL, ssname);
// Selects entity passing through point (5,5).
acedSSGet(NULL, pt2, NULL, NULL, ssname);
// Selects entities inside the window from (0,0) to (5,5).
acedSSGet("W", pt1, pt2, NULL, ssname);
// Selects entities enclosed by the specified polygon.
pt3[X] = 10.0; pt3[Y] = 5.0; pt3[Z] = 0.0;
pt4[X] = 5.0; pt4[Y] = pt4[Z] = 0.0;
pointlist = acutBuildList(RTPOINT, pt1, RTPOINT, pt2,
RTPOINT, pt3, RTPOINT, pt4, 0);
acedSSGet("WP", pointlist, NULL, NULL, ssname);
// Selects entities crossing the box from (0,0) to (5,5).
acedSSGet("C", pt1, pt2, NULL, ssname);
// Selects entities crossing the specified polygon.
acedSSGet("CP", pointlist, NULL, NULL, ssname);
acutRelRb(pointlist);
// Selects the entities crossed by the specified fence.
pt4[Y] = 15.0; pt4[Z] = 0.0;
pointlist = acutBuildList(RTPOINT, pt1, RTPOINT, pt2,
RTPOINT, pt3, RTPOINT, pt4, 0);
acedSSGet("F", pointlist, NULL, NULL, ssname);
acutRelRb(pointlist);
위 예제의 ads_name ssname 은 선택 집합 객체로 사용 후 반드시 다음 코드로 해제 해야 한다.
acedSSFree(ssname);
☆ 중요 ☆
오토캐드에서는 동시에 128개 이상의 선택 세트를 열 수 없다. 이 제한에는 동시에 실행 중인 모든 ObjectARX 및 AutoLISP 애플리케이션에서 열려 있는 선택 세트가 포함된다. 시스템에 따라 제한이 다를 수 있지만, 이 제한에 도달하면 AutoCAD는 더 이상 선택 세트를 생성하지 않는다. 많은 수의 선택 세트를 동시에 관리하는 것은 권장되지 않는다. 대신, 항상 적절한 수의 선택 세트를 열어 두고, 사용하지 않는 선택 세트는 가능한 한 빨리 acedSSFree() 함수를 호출하여 해제하는 것이 좋다. AutoLISP와 달리 ObjectARX 환경에는 사용 후 선택 세트를 자동으로 해제하는 가비지 컬렉션 기능이 없다. 애플리케이션은 kUnloadDwgMsg, kEndMsg 또는 kQuitMsg 메시지를 수신할 때마다 열려 있는 선택 세트를 해제해야 한다.
Selection Set Filter Lists
int
acedSSGet (
const char *str,
const void *pt1,
const void *pt2,
const struct resbuf *entmask,
ads_name ss);
entmask 인수에 엔티티 필드 값 목록(resbuf 링크드 리스트)이 지정되면 acedSSGet() 함수는 선택된 엔티티를 스캔하여 지정된 기준과 일치하는 모든 주요 엔티티의 이름이 포함된 선택 집합을 생성한다. 예를 들어, 이 방법을 사용하면 특정 유형, 특정 레이어 또는 특정 색상의 모든 엔티티를 포함하는 선택 집합을 얻을 수 있다.
필터(entmask)는 모든 선택 옵션과 함께 사용할 수 있다. "X" 옵션은 필터링만 사용하여 선택 집합을 생성하도록 지정한다. 이전 AutoCAD 버전과 마찬가지로 "X" 옵션을 사용하면 acedSSGet() 함수는 전체 도면 데이터베이스를 스캔한다.
참고: 필터링만 지정("X")했지만 entmask 인수가 NULL인 경우 acedSSGet() 함수는 데이터베이스의 모든 엔티티를 선택한다.
entmask 인수는 resbuf (result buffer) 목록이어야 한다. 각 버퍼는 확인할 속성과 일치하는 값을 지정한다. 버퍼의 restype 필드는 찾을 속성 유형을 나타내는 DXF 그룹 코드이고, resval 필드는 일치시킬 값을 지정한다.
struct resbuf eb1, eb2, eb3;
char sbuf1[10], sbuf2[10]; // Buffers to hold strings
ads_name ssname1, ssname2;
eb1.restype = 0; // Entity name
strcpy(sbuf1, "CIRCLE");
eb1.resval.rstring = sbuf1;
eb1.rbnext = NULL; // No other properties
// Retrieve all circles.
acedSSGet("X", NULL, NULL, &eb1, ssname1);
eb2.restype = 8; // Layer name
strcpy(sbuf2, "FLOOR3");
eb2.resval.rstring = sbuf2;
eb2.rbnext = NULL; // No other properties
// Retrieve all entities on layer FLOOR3.
acedSSGet("X", NULL, NULL, &eb2, ssname2);
각 버퍼에 지정된 resval 값은 적절한 유형이어야 한다. 예를 들어, 이름 유형은 문자열(resval.rstring)이고, 높이와 두께는 배정밀도 부동소수점 값(resval.rreal)이며, 색상, 속성 추적 및 플래그 값은 짧은 정수(resval.rint)이고, extrusion vector는 3차원 점(resval.rpoint) 이다. entmask에 둘 이상의 속성이 지정된 경우, 다음 예와 같이 지정된 모든 조건을 충족하는 경우에만 해당 엔티티가 선택 집합에 포함 된다.
eb3.restype = 62; // Entity color
eb3.resval.rint = 1; // Request red entities.
eb3.rbnext = NULL; // Last property in list
eb1.rbnext = &eb2; // Add the two properties
eb2.rbnext = &eb3; // to form a list.
// Retrieve all red circles on layer FLOOR3.
acedSSGet("X", NULL, NULL, &eb1, ssname1);
acedSSGet() 함수는 데이터베이스에서 지정된 필터링 기준과 일치하는 엔티티가 없으면 RTERROR를 반환한다.
다음은 ADS 시스템의 ads_buildlist 함수를 이용하여 LINE, LWPOLYLINE, POLYLINE, ARC, SPLINE, ELLIPSE 객체만을 선택하는 필터를 만드는 예시이다. (resbuf 생성)
struct resbuf* filter = ads_buildlist(
-4, L"<or",
RTDXF0, L"LINE",
RTDXF0, L"LWPOLYLINE",
RTDXF0, L"POLYLINE",
RTDXF0, L"ARC",
RTDXF0, L"SPLINE",
RTDXF0, L"ELLIPSE",
- 4, L"or>", NULL);
ads_name ssname;
//Keyword Callback 등록
acedSSSetKwordCallbackPtr(LoadAdOptionCallback);
const ACHAR* prompts[2] = {
_T("객체 선택 또는 [설정(T)]: "),
_T("제외할 객체 선택: ")
};
const ACHAR* keywords = _T("T_T");
// :$ 와 :K 조합
int nRet = acedSSGet(_T(":$:K"), prompts, keywords, filter, ssname);
acutRelRb(filter);
acedSSSetKwordCallbackPtr(nullptr);
Selection Set 조작
응용 프로그램을 개발하다 보면, 한번 선택한 선택 세트에 객체를 추가하거나 삭제 해야 하는 경우가 생긴다. 다음 두 함수는 선택 집합에 객체를 추가하거나 제거하는 함수이다.
- acedSSAdd()
- acedSSDel()
참고로, acedSSAdd() 함수는 다음 예제와 같이 새 선택 집합을 생성하는 데에도 사용할 수 있다. acedSSGet() 함수와 마찬가지로 acedSSAdd() 함수는 RTNORM을 반환하는 경우에만 새 선택 집합을 생성한다. 아래 예제는 데이터베이스에 엔티티가 하나만 있는 경우에도 올바르게 실행된다.
ads_name fname, lname; // Entity names
ads_name ourset; // Selection set name
// Get the first entity in the drawing.
if (acdbEntNext(NULL, fname) != RTNORM) {
acdbFail("No entities in drawing\n");
return BAD;
}
// Create a selection set that contains the first entity.
if (acedSSAdd(fname, NULL, ourset) != RTNORM) {
acdbFail("Unable to create selection set\n");
return BAD;
}
// Get the last entity in the drawing.
if (acdbEntLast(lname) != RTNORM) {
acdbFail("No entities in drawing\n");
return BAD;
}
// Add the last entity to the same selection set.
if (acedSSAdd(lname, ourset, ourset) != RTNORM) {
acdbFail("Unable to add entity to selection set\n");
return BAD;
}
//fname 객체 제거
acedSSDel(fname, ourset);
acedSSLength() 함수는 선택 집합에 있는 엔티티의 개수를 반환하고, acedSSMemb() 함수는 특정 엔티티가 선택 집합의 구성원인지 여부를 테스트한다. 마지막으로 acedSSName() 함수는 선택 집합의 인덱스(선택 집합의 엔티티는 0부터 번호가 매겨짐)를 사용하여 선택 집합에 있는 특정 엔티티의 이름을 반환한다.
선택 집합은 매우 클 수 있으므로 acedSSLength() 함수가 반환하는 len 인수는 long 정수로 선언해야 한다. acedSSName() 함수 호출에서 인덱스로 사용되는 i 인수도 long 정수여야 안전하다. int 변수로 선언할 경우 메모리 오버플로우가 발생할 수 있다. 다음은 acedSSName() 함수의 사용 예이다.
ads_name sset, ent1, ent4, lastent;
long ilast;
// Create the selection set (by prompting the user).
acedSSGet(NULL, NULL, NULL, NULL, sset);
// Get the name of first entity in sset.
if (acedSSName(sset, 0L, ent1) != RTNORM)
return BAD;
// Get the name of the fourth entity in sset.
if (acedSSName(sset, 3L, ent4) != RTNORM) {
acdbFail("Need to select at least four entities\n");
return BAD;
}
// Find the index of the last entity in sset.
if (acedSSLength(sset, &ilast) != RTNORM)
return BAD;
// Get the name of the last entity in sset.
if (acedSSName(sset, ilast-1, lastent) != RTNORM)
return BAD;
Selection Sets의 행렬 변환
acedXformSS() 함수는 선택 집합에 포함된 엔티티에 변환 행렬(ads_matrix 형식)을 적용하여 선택 집합을 변환한다.
이 함수는 acedCommandS() / acedCommandC() (또는 acedCmd() / acedCmdC())을 사용하여 ROTATE, SCALE, MIRROR 또는 MOVE 명령을 실행하거나 acdbEntMod()를 사용하여 데이터베이스 값을 변경하는 것보다 효율적인 대안을 제공한다.
선택 집합은 일반적인 방법으로 얻을 수 있다. 변환 행렬은 균일한 크기 조정을 수행해야 한다. 즉, 크기 조정 벡터 Sx Sy Sz의 요소는 모두 같아야 한다. 행렬 표기법으로 M00 M11 M22와 같다.
크기 조정 벡터가 균일하지 않으면 acedXformSS() 함수는 오류를 반환한다.
Translation

Scaling

다음 샘플 코드는 Crossing box를 사용하여 선택 집합을 가져온 다음, 해당 집합에 아래 행렬을 적용한다.

이 행렬을 적용하면 객체들의 크기가 절반으로 줄어들고 위치가 (20.0, 5.0, 0.0) 만큼 원점 쪽으로 이동한다.
int rc, i, j;
ads_point pt1, pt2;
ads_matrix matrix;
ads_name ssname;
// Initialize pt1 and pt2 here.
rc = acedSSGet("C", pt1, pt2, NULL, ssname);
if (rc == RTNORM) {
// Initialize to identity.
ident_init(matrix);
// Initialize scale factors.
matrix[0][0] = matrix[1][1] = matrix[2][2] = 0.5;
// Initialize translation vector.
matrix[0][T] = 20.0;
matrix[1][T] = 5.0;
rc = acedXformSS(ssname, matrix);
}
선택된 객체를 드래그로 이동 시키기
acedDragGen() 함수를 사용하면 선택 세트를 사용자 마우스 이동에 따라 실시간으로 변환(이동, 회전, 스케일 등) 시키는 드래그 인터랙션을 구현 할 수 있다.
#include "aced.h"
#include "adslib.h"
#include "acdb.h"
#include "dbents.h"
#include "dbsymtb.h"
/// 드래그 콜백 함수
int dragMoveCallback(
ads_point pt, // 현재 커서 위치
ads_matrix mat // 변환 행렬 (출력)
)
{
static ads_point basePt = {0, 0, 0};
// 이동 벡터 계산
ads_point vec;
vec[0] = pt[0] - basePt[0];
vec[1] = pt[1] - basePt[1];
vec[2] = pt[2] - basePt[2];
// 단위 행렬 초기화
acutIdentMatrix(mat);
// 이동 적용
mat[0][3] = vec[0];
mat[1][3] = vec[1];
mat[2][3] = vec[2];
return RTNORM;
}
void cmd_DragMove()
{
ads_name ss;
// 1. 객체 선택
if (acedSSGet(NULL, NULL, NULL, NULL, ss) != RTNORM)
{
acutPrintf(L"\n객체 선택 실패");
return;
}
// 2. 기준점 입력
ads_point basePt;
if (acedGetPoint(NULL, L"\n기준점 지정: ", basePt) != RTNORM)
return;
// static 변수에 저장 (콜백에서 사용)
extern int dragMoveCallback(ads_point, ads_matrix);
*(ads_point*)(&dragMoveCallback + 1); // (설명용, 실제로는 전역/static 변수로 관리)
// 3. 드래그 수행
int rc = acedDragGen(
ss, // 선택 세트
L"\n이동할 위치 지정: ",
NULL, // 키워드 없음
dragMoveCallback // 콜백
);
if (rc == RTNORM)
{
acutPrintf(L"\n이동 완료");
}
else if (rc == RTCAN)
{
acutPrintf(L"\n취소됨");
}
// 4. 선택 세트 해제 (중요)
acedSSFree(ss);
}
int dragMoveCallback(ads_point pt, ads_matrix mat)
- pt : 현재 커서 위치
- mat : 변환 행렬 (이걸 계속 갱신해야 움직이는 임시 객체를 생성 할 수 있음)
acedDragGen 특징
- 실제 DB를 수정하지 않음 (미리보기 용 임시객체 생성)
- ENTER 또는 클릭 시 확정
- ESC시 취소
'ObjectARX 강좌' 카테고리의 다른 글
| [ObjectARX] 9. Demand Loading (1) | 2026.04.07 |
|---|---|
| [ObjectARX] 8. Proxy (0) | 2026.04.06 |
| [ObjectARX] 6. Dictionary (0) | 2026.03.27 |
| [ObjectARX] 5. GS Marker (0) | 2026.03.23 |
| [ObjectARX] 4. Symbol Table (0) | 2026.03.22 |