Notice
Link
- Today
- Total
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 가변영역 스크롤
- Activity 전체화면
- springboot 재가동
- view 획득
- reactnative
- sql exception
- ffmpeg
- c언어
- kill -9
- 시간대별 통계
- group by
- CentOS
- springboot
- Back 키 클릭 감지
- SQL
- MariaDB
- mybatis exception
- 시간대 테이블생성
- 말줌임 CSS
- vc++
- 터치좌표 view
- MFC
- 스크롤적용
- rn
- 파티션 빠른 삭제
- 코드로 서버 재실행
- MySQL
- pid 찾아 kill
- CSS
- 피쉬랜드
Archives
개발은 하는건가..
[MFC] 가상 조이스틱 패드 컨트롤 본문
반응형
카메라의 Pan Tilt 조정에 모바일 게임에서 사용되는 가상 조이스틱 패드 같은 컨트롤이 필요하여 만들어 보았다.
사용방법
// GDI+ 를 사용하므로 Application 클래스에서 GDI+ 를 사용할 수 있도록 초기화 한다.
// 리소스 편집기에서 Static 컨트롤을 올리고 컨트롤 변수를 생성하여 CPantTiltCtrl 로 서브클래싱한다.
//CStatic m_PanTiltCtrl;
CPanTiltCtrl m_PanTiltCtrl;
// 컨트롤 변수를 통해 초기화 함수 호출
m_PanTiltCtrl.InitControl(GetAppImagePath(), this);
m_PanTiltCtrl.SetBackgroundColor(RGB(32, 35, 54));
컨트롤에서 사용되는 2개의 png 파일이 필요하다.
InitControl() 함수의 첫번째 파라메터는 이미지 파일이 있는 경로를 지정해 준다.
BG_PTZ_PANEL.png - 컨트롤 배경 판넬용 이미지
IMG_PTZ_JOG.png - 컨트롤 스틱 이미지
헤더 파일
#pragma once
#include <afxwin.h>
#define RESPONSE_SENSIT 40
#define TMR_TILT_ACTION 1000
#define TMR_PAN_ACTION 2000
#define MSG_PTZ_CTRL_B WM_USER + 4002
#define ACTB_TILT_NONE 100
#define ACTB_TILT_UP 101
#define ACTB_TILT_DOWN 102
#define ACTB_TILT_STOP 103
#define ACTB_PAN_NONE 200
#define ACTB_PAN_LEFT 201
#define ACTB_PAN_RIGHT 202
#define ACTB_PAN_STOP 203
#define PI 3.1415
class CPanTiltCtrl :public CStatic
{
public:
void InitControl(CString strImagePath, CWnd *pParent);
void EnableControl(BOOL bEnabled);
void SetBackgroundColor(COLORREF clr);
CString ActionToString(UINT act);
inline BOOL IsEnableControl() { return m_bEnabled; }
private:
BOOL m_bInit = FALSE;
BOOL m_bEnabled = TRUE;
Gdiplus::Image *m_pBaseImage = NULL;
Gdiplus::Image *m_pJogImage = NULL;
CWnd *m_pParent = NULL;
int m_nPosX = 0, m_nPosY = 0;
int m_nCenterX = 0, m_nCenterY = 0;
int m_nWidth = 0, m_nHeight = 0;
int m_nMoveMode = -1;
BOOL m_bDragging = FALSE;
int m_nTiltActionType = ACTB_TILT_NONE;
int m_nTiltStrength = 0;
int m_nPanActionType = ACTB_PAN_NONE;
int m_nPanStrength = 0;
int m_nPrevTiltActionType = 0;
int m_nPrevTiltStrength = 0;
int m_nPrevPanActionType = 0;
int m_nPrevPanStrength = 0;
int m_nDeltaX = 0;
int m_nDeltaY = 0;
COLORREF m_clBgColor = RGB(0, 0, 0);
void UpdateCurrentPos();
void DrawImage(Gdiplus::Graphics *pGrs, Gdiplus::Image *pImg, int x, int y, float alpha);
double GetDistancePoints(POINT *p1, POINT *p2);
void NotifyEvent(int actType, int strength = 0);
public:
DECLARE_MESSAGE_MAP()
virtual void PreSubclassWindow();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnDestroy();
afx_msg void OnNcPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnPaint();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnTimer(UINT_PTR nIDEvent);
};
cpp 파일
#include "../pch.h"
#include "PanTiltCtrl.h"
const CRect gRectCenter(100, 101, 146, 146);
BEGIN_MESSAGE_MAP(CPanTiltCtrl, CWnd)
ON_WM_CREATE()
ON_WM_DESTROY()
ON_WM_NCPAINT()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_TIMER()
END_MESSAGE_MAP()
int CPanTiltCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
void CPanTiltCtrl::PreSubclassWindow()
{
DWORD dwStyle = GetStyle();
::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);
ModifyStyleEx(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE, NULL, SWP_FRAMECHANGED);
CStatic::PreSubclassWindow();
}
void CPanTiltCtrl::InitControl(CString strImagePath, CWnd *pParent)
{
if (m_bInit == TRUE) {
return;
}
m_pParent = pParent;
m_pBaseImage = Gdiplus::Image::FromFile(strImagePath + _T("\\BG_PTZ_PANEL.png"));
m_pJogImage = Gdiplus::Image::FromFile(strImagePath + _T("\\IMG_PTZ_JOG.png"));
if (m_pBaseImage != NULL && m_pBaseImage->GetLastStatus() == Gdiplus::Ok) {
UINT w = m_pBaseImage->GetWidth();
UINT h = m_pBaseImage->GetHeight();
SetWindowPos(NULL, 0, 0, w + 1, h + 1, SWP_NOMOVE);
}
m_bInit = TRUE;
}
void CPanTiltCtrl::EnableControl(BOOL bEnabled)
{
m_bEnabled = bEnabled;
Invalidate();
}
void CPanTiltCtrl::OnDestroy()
{
CWnd::OnDestroy();
}
void CPanTiltCtrl::OnNcPaint()
{
// Nop
}
void CPanTiltCtrl::DrawImage(Gdiplus::Graphics *pGrs, Gdiplus::Image *pImg, int x, int y, float alpha)
{
const Gdiplus::ColorMatrix colorMatrix = {
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, alpha, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
};
Gdiplus::ImageAttributes imageAtt;
imageAtt.SetColorMatrix(&colorMatrix,
Gdiplus::ColorMatrixFlagsDefault,
Gdiplus::ColorAdjustTypeBitmap);
pGrs->DrawImage(
pImg,
Gdiplus::Rect(x, y, pImg->GetWidth(), pImg->GetHeight()),
0, 0, pImg->GetWidth(), pImg->GetHeight(),
Gdiplus::UnitPixel,
&imageAtt);
}
BOOL CPanTiltCtrl::OnEraseBkgnd(CDC* pDC)
{
CMemDC memDC(*pDC, this);
pDC = &memDC.GetDC();
pDC->FillSolidRect(CRect(0, 0, m_nWidth, m_nHeight), m_clBgColor);
Gdiplus::Graphics grs(pDC->GetSafeHdc());
if (m_pBaseImage != NULL) {
if (m_bEnabled == TRUE) {
DrawImage(&grs, m_pBaseImage, 0, 0, 1.0f);
if (m_pJogImage != NULL && m_pJogImage->GetLastStatus() == Gdiplus::Ok) {
int posX = m_nCenterX + m_nPosX - (m_pJogImage->GetWidth() / 2);
int posY = m_nCenterY + m_nPosY - (m_pJogImage->GetHeight() / 2);
grs.DrawImage(m_pJogImage,
Gdiplus::Rect(posX, posY, m_pJogImage->GetWidth(), m_pJogImage->GetHeight()),
0, 0, m_pJogImage->GetWidth(), m_pJogImage->GetHeight(),
Gdiplus::UnitPixel);
}
}
else {
DrawImage(&grs, m_pBaseImage, 0, 0, 0.7f);
}
}
return FALSE;
}
void CPanTiltCtrl::OnPaint()
{
CPaintDC dc(this);
}
void CPanTiltCtrl::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
m_nCenterX = cx / 2;
m_nCenterY = cy / 2;
m_nWidth = cx;
m_nHeight = cy;
}
void CPanTiltCtrl::UpdateCurrentPos()
{
InvalidateRect(CRect(0, 0, m_nWidth, m_nHeight), TRUE);
}
void CPanTiltCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_bEnabled == FALSE) {
return;
}
if (gRectCenter.PtInRect(point) == TRUE) {
SetCapture();
m_bDragging = TRUE;
m_nDeltaX = point.x - m_nCenterX;
m_nDeltaY = point.y - m_nCenterY;
TRACE(_T("Down :: CenterIn \n"));
}
CWnd::OnLButtonDown(nFlags, point);
}
void CPanTiltCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bDragging == FALSE) {
CWnd::OnMouseMove(nFlags, point);
return;
}
KillTimer(TMR_TILT_ACTION);
KillTimer(TMR_PAN_ACTION);
if (m_nTiltActionType != ACTB_TILT_NONE) {
NotifyEvent(ACTB_TILT_STOP);
}
if (m_nPanActionType != ACTB_PAN_NONE) {
NotifyEvent(ACTB_PAN_STOP);
}
m_nPosX = 0;
m_nPosY = 0;
m_nTiltActionType = ACTB_TILT_NONE;
m_nTiltStrength = 0;
m_nPanActionType = ACTB_PAN_NONE;
m_nPanStrength = 0;
m_nPrevTiltActionType = 0;
m_nPrevTiltStrength = 0;
m_nPrevPanActionType = 0;
m_nPrevPanStrength = 0;
m_nDeltaX = 0;
m_nDeltaY = 0;
m_bDragging = FALSE;
UpdateCurrentPos();
ReleaseCapture();
CWnd::OnLButtonUp(nFlags, point);
}
void CPanTiltCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_bDragging == FALSE) {
CWnd::OnMouseMove(nFlags, point);
return;
}
if (point.x < 0 || point.x > m_nWidth || point.y < 0 || point.y > m_nHeight) {
CWnd::OnMouseMove(nFlags, point);
return;
}
point.x -= m_nDeltaX;
point.y -= m_nDeltaY;
const int RADIUS = 90;
const int STOP_AREA_WIDTH = 8;
double ptDistance = GetDistancePoints(&CPoint(m_nCenterX, m_nCenterY), &point);
if (ptDistance > RADIUS) {
// 포인팅 지점의 각도 계산
int relX, relY;
relX = point.x - m_nCenterX;
relY = point.y - m_nCenterY;
double ptDeg = atan2(relX, relY) * 180 / PI;
//TRACE(_T("Degree x=%d, y=%d, %.1f \n"), relX, relY, ptDeg);
// 해당 각도로 원점으로 부터 반경 거리에 위치한 좌표 계산
double radian = ptDeg * PI / 180.0f;
double dx = sin(radian) * RADIUS;
double dy = cos(radian) * RADIUS;
m_nPosX = (int)dx;
m_nPosY = (int)dy;
}
else {
m_nPosY = point.y - m_nCenterY;
m_nPosX = point.x - m_nCenterX;
}
// TILT ACTION 판별
if (abs(m_nPosY) < STOP_AREA_WIDTH) {
if (m_nTiltActionType != ACTB_TILT_NONE) {
m_nTiltActionType = ACTB_TILT_STOP;
m_nTiltStrength = 0;
SetTimer(TMR_TILT_ACTION, RESPONSE_SENSIT, NULL);
}
}
else {
m_nTiltStrength = ((abs(m_nPosY) - STOP_AREA_WIDTH) * 10 / RADIUS) + 1;
m_nTiltStrength = min(10, m_nTiltStrength);
if (m_nPosY < 0) {
m_nTiltActionType = ACTB_TILT_UP;
}
else if (m_nPosY > 0) {
m_nTiltActionType = ACTB_TILT_DOWN;
}
SetTimer(TMR_TILT_ACTION, RESPONSE_SENSIT, NULL);
}
// PAN ACTION 판별
if (abs(m_nPosX) < STOP_AREA_WIDTH) {
if (m_nPanActionType != ACTB_PAN_NONE) {
m_nPanActionType = ACTB_PAN_STOP;
m_nPanStrength = 0;
SetTimer(TMR_PAN_ACTION, RESPONSE_SENSIT, NULL);
}
}
else {
m_nPanStrength = ((abs(m_nPosX) - STOP_AREA_WIDTH) * 10 / RADIUS) + 1;
m_nPanStrength = min(10, m_nPanStrength);
if (m_nPosX < 0) {
m_nPanActionType = ACTB_PAN_LEFT;
}
else if (m_nPosX > 0) {
m_nPanActionType = ACTB_PAN_RIGHT;
}
SetTimer(TMR_PAN_ACTION, RESPONSE_SENSIT, NULL);
}
UpdateCurrentPos();
CWnd::OnMouseMove(nFlags, point);
}
void CPanTiltCtrl::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
if (nIDEvent == TMR_TILT_ACTION) {
NotifyEvent(m_nTiltActionType, m_nTiltStrength);
}
else if (nIDEvent == TMR_PAN_ACTION) {
NotifyEvent(m_nPanActionType, m_nPanStrength);
}
CStatic::OnTimer(nIDEvent);
}
void CPanTiltCtrl::NotifyEvent(int actType, int strength)
{
// TILT_ACTION 일 경우 중복 이벤트 방지
if ((int)(actType / ACTB_TILT_NONE) == 1) {
if (m_nPrevTiltActionType == actType && m_nPrevTiltStrength == strength) {
return;
}
m_nPrevTiltActionType = actType;
m_nPrevTiltStrength = strength;
}
// PAN_ACTION 일 경우 중복 이벤트 방지
if ((int)(actType / ACTB_PAN_NONE) == 1) {
if (m_nPrevPanActionType == actType && m_nPrevPanStrength == strength) {
return;
}
m_nPrevPanActionType = actType;
m_nPrevPanStrength = strength;
}
if (m_pParent != NULL) {
m_pParent->PostMessageW(MSG_PTZ_CTRL_B, actType, strength);
}
#ifdef _DEBUG
if (actType == ACTB_TILT_UP) {
TRACE(_T("ActionType=%d(TILT_UP) Strength=%d\n"), actType, strength);
}
else if (actType == ACTB_TILT_DOWN) {
TRACE(_T("ActionType=%d(TILT_DOWN) Strength=%d\n"), actType, strength);
}
else if (actType == ACTB_TILT_STOP) {
TRACE(_T("ActionType=%d(TILT_STOP) \n"), actType);
}
else if (actType == ACTB_PAN_LEFT) {
TRACE(_T("ActionType=%d(PAN_LEFT) Strength=%d\n"), actType, strength);
}
else if (actType == ACTB_PAN_RIGHT) {
TRACE(_T("ActionType=%d(PAN_RIGHT) Strength=%d\n"), actType, strength);
}
else if (actType == ACTB_PAN_STOP) {
TRACE(_T("ActionType=%d(PAN_STOP) \n"), actType);
}
#endif
}
void CPanTiltCtrl::SetBackgroundColor(COLORREF clr)
{
m_clBgColor = clr;
Invalidate();
}
CString CPanTiltCtrl::ActionToString(UINT act)
{
if (act == ACTB_TILT_UP) {
return _T("TILT_UP");
}
else if (act == ACTB_TILT_DOWN) {
return _T("TILT_DOWN");
}
else if (act == ACTB_PAN_LEFT) {
return _T("PAN_LEFT");
}
else if (act == ACTB_PAN_RIGHT) {
return _T("PAN_RIGHT");
}
else if (act == ACTB_TILT_STOP) {
return _T("TILT_STOP");
}
else if (act == ACTB_PAN_STOP) {
return _T("PAN_STOP");
}
return _T("");
}
double CPanTiltCtrl::GetDistancePoints(POINT *p1, POINT *p2)
{
double distance;
distance = sqrt(pow(p1->x - p2->x, 2) + pow(p1->y - p2->y, 2));
//TRACE(_T("Distance point = %.3f\n"), distance);
return distance;
}
'C, C++, MFC' 카테고리의 다른 글
[MFC] 배율에 따라 영향 받지 않도록 컨트롤 배치 (0) | 2022.11.09 |
---|---|
[WinAPI] VC++ 윈도우 화면 배율 가져오기 (0) | 2022.11.09 |
특정 좌표의 원점 기준 각도 계산 관련 (0) | 2022.10.25 |
[MFC] 오른쪽 버튼 메뉴 띄우기 (custom contextmenu) (1) | 2022.09.14 |
[VC++] 화면을 동영상으로 저장하기 (0) | 2022.09.08 |
Comments