.트래커 그리기

OnPaint에서 선택된 도형의 상태는 다른 도형과 조금 다르게 그려야 하는데 통상 선택된 도형 주변에는 8개의 트래커를 그린다. 트래커는 도형이 선택되었다는 것을 보여줌과 동시에 크기 변경을 위한 드래그 핸들로도 사용된다. 다음 매크로 상수는 트래커의 크기를 지정한다. 관행적으로 매크로 정의문은 #include 다음에 정의하는 것이 좋다.

 

// 매크로 정의

#define TRSIZE 4

 

트래커는 보통 정사각형으로 그리는데 TRSIZE 정사각형의 길이의 절반을 정의한다. 트래커가 너무 작으면 불편하며 너무 크면 멋이 없어 적절한 크기를 선택해야 하는데 크기 변경을 쉽게 있도록 매크로 상수로 정의했다. 다음 함수는 선택된 도형의 트래커를 화면에 그린다.

 

void DrawTracker(HDC hdc,int idx)

{

   RECT rt;

   int i;

 

   if (idx == -1) return;

   for (i=1;i<=8;i++) {

      GetTrackerRect(idx,i,&rt);

      Rectangle(hdc,rt.left,rt.top,rt.right,rt.bottom);

   }

}

 

void GetTrackerRect(int idx,int nTrack,RECT *trt)

{

   int tx,ty;

   RECT *ort=&arObj[idx]->rt;

   switch (nTrack) {

   case 1:

      tx=ort->left;

      ty=ort->top;

      break;

   case 2:

      tx=ort->left+(ort->right-ort->left)/2;

      ty=ort->top;

      break;

   case 3:

      tx=ort->right;

      ty=ort->top;

      break;

   case 4:

      tx=ort->left;

      ty=ort->top+(ort->bottom-ort->top)/2;

      break;

   case 5:

      tx=ort->right;

      ty=ort->top+(ort->bottom-ort->top)/2;

      break;

   case 6:

      tx=ort->left;

      ty=ort->bottom;

      break;

   case 7:

      tx=ort->left+(ort->right-ort->left)/2;

      ty=ort->bottom;

      break;

   case 8:

      tx=ort->right;

      ty=ort->bottom;

      break;

   }

   SetRect(trt,tx-TRSIZE,ty-TRSIZE,tx+TRSIZE,ty+TRSIZE);

}

 

도형 하나의 주변에는 8개의 트래커가 그려지는데 좌에서 우로 위에서 아래로 트래커의 번호를 다음과 같이 부여해 두었다. 번호는 프로그램 내부에서 트래커를 칭하기 위한 일종의 ID 사용된다. 번호를 쓰는 대신 #define이나 열거형으로 TR_LEFT, TR_RIGHTTOP, TR_RIGHT 등의 상수를 정의하여 사용할 수도 있다. 그러나 경우는 순서대로 번호를 붙였으므로 굳이 이름을 주지 않더라도 관리하기 어렵지 않으며 루프를 돌릴 오히려 편리하다.

GetTrackerRect 함수는 도형의 번호와 트래커 번호(nTrack) 입력으로 받아들여 트래커의 영역을 번째 출력 인수 trt 대입한다. 도형의 영역을 ort 지역 변수에 구해 놓고 nTrack 따라 ort 꼭지점 또는 변의 중앙점의 좌표를 tx, ty 먼저 구하고 tx, ty 중심으로 하는 TRSIZE*2 크기의 정사각형 좌표를 구하면 된다. tx, ty에서 +TRSIZE, -TRSIZE 영역을 트래커 영역으로 정의하므로 TRSIZE 트래커 크기의 절반이 되는 것이다.

함수가 트래커의 좌표를 구하는 작업을 전담하고 있으므로 DrawTracker 함수는 주어진 도형에 대해 1~8까지 루프를 돌며 트래커 좌표를 구하고 Rectangle 함수로 사각형만 그리면 된다. 만약 트래커를 동그랗게 그리고 싶으면 Ellipse 그리면 되고 , 브러시를 사용하면 컬러풀하게 그릴 수도 있고 크게 그리고 싶으면 TRSIZE 매크로 상수를 조정한다. OnPaint 선택 영역이 있을 DrawTracker 함수만 호출하면 된다.

 

LRESULT OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   ....

   if (NowSel != -1) {

      DrawTracker(hdc,NowSel);

   }

   EndPaint(hWnd, &ps);

   return 0;

}

 

트래커를 그리는 관련 함수들의 역할 분담이 되어 있다. GetTrackerRect 함수는 이후 트래커 위에서 커서 모양을 변경하는 OnSetCursor 함수에서도 사용되며 도형 크기를 변경할 때도 사용된다. 여러 사용될 함수이므로 OnPaint DrawTracker에서 작업을 직접 처리하지 않고 별도의 함수로 분리해 것이다. 이런 함수의 분리는 처음부터 생각하기는 어렵지만 프로젝트를 진행하다 보면 분리하는 것이 좋다는 것을 쉽게 발견할 있다. 여기까지 코드를 작성한 테스트해 보면 선택 상태가 화면에 표시될 것이다.

프로그램은 선택을 다른 도형 그리기와 대등한 모드로 간주하기 때문에 선택을 하려면 반드시 선택 툴로 바꿔야 하는 불편함이 있다. 도형을 그리는 중에라도 영역을 클릭하면 선택 툴로 자동 전환하도록 보자. AppendObject 함수는 도형이 추가되었는지 그냥 클릭만 되었는지를 리턴값으로 알려 준다. 도형이 추가되었다면 방금 그린 도형을 선택된 도형으로 만들고 그렇지 않다면 여백을 클릭한 것으로 간주하여 선택 툴로 자동 전환하도록 보자.

 

LRESULT OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   if (DragMode==DM_DRAW) {

      if (AppendObject(NowTool,sx,sy,oldx,oldy)==TRUE) {

          NowSel=arNum-1;

      } else {

          NowTool=DT_SELECT;

          NowSel=-1;

      }

      InvalidateRect(hWnd,NULL,TRUE);

   }

   DragMode=DM_NONE;

   ReleaseCapture();

   return 0;

}

 

AppendObject 새로 추가된 도형을 항상 배열 끝에 추가하므로 새로 추가된 도형의 첨자는 arNum-1 쉽게 구할 있다. 그래서 도형이 성공적으로 추가되었으면 arNum-1 선택 상태로 만든다. 도형 추가에 실패했으면 선택은 해제되고 선택 모드로 자동 전환한다. 도형을 그리다가 선택툴로 전환하고 싶으면 메뉴에서 선택 도구로 전환할 필요없이 바닥을 털어주듯이 영역을 클릭하기만 하면 된다.

이런 방법 외에 도형 하나를 그린 자동으로 선택툴로 전환하도록 수도 있는데 이렇게 되면 도형 추가 이동이나 크기 변경을 바로 있어 편리하기는 하지만 같은 모양의 도형을 반복적으로 그릴 때는 매번 툴을 선택해야 하므로 오히려 불편한 면도 있다. 선택툴 전환 정책(Policy) 같은 도형의 반복 추가 빈도에 따라 결정해야 한다. 가장 좋은 방법은 선택툴로 자동 전환하는 여러 가지 방법을 제공하고 사용자가 필요에 따라 옵션을 선택할 있도록 하는 것이다.