NG 체계에서 CURSOR를?

댓글 0

IDL/New Graphics

2019. 3. 25.

IDL에서 CURSOR라는 명령을 사용해 본 유저들이 꽤 있으실 겁니다. 그래픽 창을 띄워놓은 상태에서 마우스 버튼을 특정 위치에서 클릭하면 그 위치의 좌표값들을 되돌려주는 역할을 하기 때문에 간간이 유용하게 사용 가능한 명령이기도 합니다. 그런데 이 CURSOR는 요즘 시대에 와서는 큰 단점이 하나 생겨버렸는데, 바로 DG(Direct Graphics) 체계에서만 사용이 가능하다는 것입니다. 즉 NG(New Graphics) 체계에서는 사용이 불가능합니다. 원래부터 DG 체계에서만 사용 가능하도록 옛날 IDL 초창기에 만들어진 기능이기 때문입니다. 그리고 NG 체계에서는 이와 유사한 기능의 명령이 지원되지 않고 있는 상황이기도 합니다. 그래서 유저들 입장에서는 예전부터 편리하게 사용해왔던 CURSOR 명령을 NG에서는 왜 사용할 수 없을까? 혹은 비슷한 대체수단은 없는 것인가? 등에 대하여 궁금하게 생각하는 것이 당연합니다.


사실 NG 체계의 그래픽 창에서도 마우스 포인터의 위치를 보는 것은 가능합니다. 그냥 IDL 커맨드 입력창에서 다음과 같은 간단한 명령을 실행하여 플롯을 하나 표출한 후 그래픽창 위에서 마우스 포인터를 움직여보면 그래픽창의 우측 하단에 위치 좌표가 인터액티브하게 표시가 됩니다.


IDL> p = PLOT(/TEST)


그러나 이러한 방식에서는 마우스 버튼을 클릭하여 그 지점의 좌표를 뽑아내지는 못합니다. 즉 클릭한 지점의 좌표값을 변수를 통하여 되돌려 받지는 못한다는 의미입니다. 물론 마우스 포인터의 위치는 그래픽창에서 어차피 표시가 되기 때문에 그 좌표는 분명히 IDL이 알고 있는 것은 맞습니다. 다만 그러한 좌표를 그냥 그래픽창에서 보는 것은 가능하지만 그 값을 바로 추출하지는 못합니다. 좌표값의 추출은 DG 체계에서는 CURSOR 프로시저로 처리할 수 있었지만, NG 체계에서는 그런 역할을 해줄만한 유사한 명령이 아직 없습니다. 물론 그렇다고 NG 체계에서 이러한 작업이 아예 불가능한 것은 아닙니다. 다만 이를 위해서는 NG 체계의 그래픽창을 구현하는 WINDOW 함수에서 지원되는 마우스 입력 관련 이벤트들에 대한 처리 작업 내용을 명시적으로 지정해줘야 하는데, 그러려면 이러한 기능들에 대한 이해를 바탕으로 코딩을 직접 해줘야한다는 불편함이 따릅니다. 이 작업이 사실 만만치가 않아서, 일반 유저들에게 권하기는 힘든 것도 사실입니다. 그리고 저도 이 문제에 대하여 전에도 고민을 좀 해본 적이 있는데, 뭔가 자꾸 막히는 부분이 있어서 실패를 계속 했었습니다.


그런데 다행히도 이번에 뭔가 좀 쓸만한 해결책을 찾은 덕분에 비슷한 느낌의 루틴을 하나 만들어볼 수 있었습니다. 물론 DG의 CURSOR와 완벽히 동일하지는 않고, 나름의 장단점이 존재합니다. 일단 이 루틴의 이름은 CURSOR_NG입니다. 바로 아래에 첨부합니다. 링크를 통하여 바로 다운로드받아서 사용해보시면 됩니다.


cursor_ng.pro


가장 큰 특징이자 어쩌면 단점이 될 수도 있는 부분을 먼저 언급한다면, 이 CURSOR_NG 명령은 IDL의 커맨드 입력창 모드에서만 제대로 사용이 가능합니다. 즉 프로시저의 내부에서 사용될 경우에는 정상적인 역할을 할 수가 없습니다. 따라서 사용 예제는 다음과 같이 커맨드 입력 모드에서 실행되도록 하였습니다. 먼저 이미지로 표출 가능한 가상의 2차원 데이터를 생성하고 NG 그래픽창을 띄워서 이미지의 형태로 표출하는 과정부터 다음과 같이 차례로 실행해 봅시다.


IDL> data = HANNING(600, 600)*10

IDL> win = WINDOW(DIMENSIONS=[600, 600], /NO_TOOLBAR)

IDL> im = IMAGE(data, MARGIN=0, /CURRENT)


이 그래픽창이 떠있는 상태에서 커맨드 입력창에서 다음과 같이 CURSOR_NG 명령을 사용합니다.


IDL> CURSOR_NG, win, xc, yc


CURSOR_NG 명령은 이와 같이 세개의 인수들을 받도록 되어 있습니다. 첫번째 인수는 NG 그래픽창의 이름입니다. 여기서는 win이 될 것입니다. 그리고 두번째 및 세번째 인수는 마우스 클릭이 된 지점의 X, Y 좌표값을 받아들일 적절한 이름의 변수 두 개를 차례로 주면 됩니다.여기서는 xc, yc라는 변수를 통해서 받아올 생각입니다. 이 명령을 실행한 후 그래픽창의 임의의 지점에서 마우스 왼쪽 버튼을 클릭하면 커맨드 입력창에 그 지점의 좌표가 클릭할 때마다 출력될 것입니다. 예를 들면 다음과 같은 모습입니다.


IDL> CURSOR_NG, win, xc, yc

         145         384

         384         232

          89          94

         482         497


원하는 지점에 대한 클릭이 더 이상 필요 없어지면 그래픽창을 그냥 닫으면 됩니다. 사실 그래픽창을 닫느냐 마느냐 여부에 상관없이 매번 클릭할 때마다 좌표의 값은 xc, yc에 저장이 됩니다. 이 때 맨 마지막에 클릭했던 지점의 좌표값들이 저장된 상태가 됩니다. 따라서 xc, yc의 값을 출력해보면 X, Y 좌표값들을 볼 수 있어야 할텐데요. 하지만 xc, yc를 출력해보면 다음과 같이 이상한 정보들이 출력됩니다.


IDL> PRINT, xc, yc

<PtrHeapVar21724><PtrHeapVar21725>


그 이유는 xc, yc는 그냥 변수가 아니라 포인터(Pointer)이기 때문입니다. 이렇게 X, Y 좌표를 바로 값으로 돌려받지 않고 포인터의 형태로 받을 수 밖에 없는 피치못할 이유가 있습니다만, 이 부분은 좀 복잡한 얘기라서 여기서는 제가 생략을 하겠습니다. 다만 우리가 알아볼 수 있는 모습으로 좌표값들을 가져오려면 다음과 같은 문법을 사용해야 한다는 정도만 언급하기로 하겠습니다.


IDL> PRINT, *xc, *yc

         482         497


참고로 이러한 경우를 포인터를 디레퍼런스(De-reference)한다고 얘기합니다. 포인터가 저장하고 있는 값을 직접 꺼내오는 문법이라고 생각하시면 됩니다.


그리고 이 CURSOR_NG의 기능은 또 한가지가 있는데요. 마우스 왼쪽 버튼 뿐 아니라 오른쪽 버튼을 클릭하여 좌표값을 가져오는 것도 가능합니다. 사실 제가 기능을 두 종류가 구분해놓은 것인데, 왼쪽 버튼을 클릭하면 DEVICE 좌표계 기준의 좌표값(픽셀 단위)을 가져오고 오른쪽 버튼을 클릭하면 DATA 좌표계 기준의 좌표값(표출된 데이터 단위)을 가져오도록 하였습니다. 두 기능의 차이를 명확히 보기 위하여 다음과 같이 플롯을 예제로 한번 표출해 봅시다.


IDL> x = FINDGEN(101)

IDL> y = SQRT(x)

IDL> win = WINDOW(DIMENSIONS=[600, 500], /NO_TOOLBAR)

IDL> p = PLOT(x, y, COLOR='blue', THICK=2, /CURRENT)


이 상태에서 CURSOR_NG 명령을 다음과 같이 실행합니다.


IDL> CURSOR_NG, win, xc, yc


이 상태에서 그래픽창의 임의의 지점에서 마우스 왼쪽 버튼을 클릭하면 다음과 같이 DEVICE 좌표계 기준의 좌표가 출력됩니다. 이 좌표는 그 위치의 좌표를 픽셀 단위로 나타낸 것입니다.


IDL> CURSOR_NG, win, xc, yc

         445         231


그런데 마우스 포인터의 위치를 바꾸지 않은 상태에서 이번엔 마우스 오른쪽 버튼을 클릭하면 다음과 같이 전혀 다른 좌표값이 출력됩니다.


IDL> CURSOR_NG, win, xc, yc

         445         231

       77.493849       4.4419514


이 좌표는 동일한 지점에 대한 X축 및 Y축 기준의 좌표값입니다. 이와 같이 마우스 버튼의 왼쪽 오른쪽 여부에 따라 DEVICE, DATA 좌표계를 구분하여 좌표값을 얻을 수 있습니다. 이와 같은 방식으로 이용하면 됩니다.


따라서 CURSOR_NG 명령은 NG 체계에서 표출 가능한 여러 종류의 그래픽 컨텐츠들에 대하여 폭넓게 이용이 가능할 것 같습니다. 다만 이 명령은 지금 예제로 보여드린 바와 같이 IDL의 커맨드 입력 모드에서만 제대로 사용이 가능합니다. 프로그램의 내부에 삽입을 해서는 원래의 취지대로 사용을 할 수가 없습니다. 무슨 얘기냐 하면 예전의 CURSOR 프로시저의 경우는 프로그램 내부에서 사용될 경우에는 그 위치에서 커서 좌표 입력을 한번 받아야 실행이 종료되어서 그 다음 단계로 진행이 됩니다. 즉 CURSOR 기능이 완전히 종료되기 전까지는 다음 단계로의 진행이 일시적으로 멈춰지도록 되어 있습니다. 따라서 CURSOR를 통하여 입력받은 xc, yc를 프로그램의 이후 내용에서 활용하는 것이 가능합니다. 그런데 지금 소개하는 CURSOR_NG의 경우는 이 기능이 활성화된 상태라고 해서 프로그램이 그 위치에서 멈취지지 않습니다. 프로그램 내에서 그 위치 이후의 내용이 그냥 일사천리로 진행됩니다. 즉 CURSOR_NG를 통하여 입력받은 xc, yc를 프로그램의 뒷부분에서 활용하는 것이 불가능합니다. 설명이 좀 장황하긴 한데 어떤 의미인지 제대로 전달이 되었을라나 모르겠습니다.


사실 이러한 한계점은 NG 체계의 그래픽창을 생성하는 WINDOW 함수의 특성 때문입니다. 이러한 한계점을 극복하려면 그냥 WINDOW 함수가 아닌 WIDGET_WINDOW 함수를 사용하여 구현을 해야 하는데, 사실 이렇게 하더라도 완벽해지지는 않을 것 같습니다. 이 부분에 대해서는 제가 여기서 더 언급하기는 힘들 것 같습니다. 그래서 나중에라도 좀 더 개선이 가능해질 경우에는 업데이트를 할 예정입니다만, 이게 가능할지 또는 가능하다면 언제가 될 것인지에 대해서는 아직은 기약할 수 없는 상황입니다. 다만 나중에라도 WIDGET_WINDOW 함수에 관하여 소개를 할 일이 있을 것 같은데, 기술적인 부분에 대해서는 그 때 언급을 해보기로 하겠습니다.


그리고 오늘 소개한 CURSOR_NG의 사용법과 관련된 짧은 영상을 만들어본 것이 있어서 링크를 함께 첨부합니다.



- 첨부파일

cursor_ng.pro