// CCurveWnd : implementation file // // CurveWnd Class, visual representaion of the curve. // Functionality : // 1. Draw Knots, Curve // 2. Handle mouse and keyboard input // // Copyright Johan Janssens, 2001 (jjanssens@mail.ru) // Feel free to use and distribute. May not be sold for profit. // // This code may be used in compiled form in any way you desire. This // file may be redistributed unmodified by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. // If the source code in this file is used in any commercial application // then acknowledgement must be made to the author of this file // // This file is provided "as is" with no expressed or implied warranty. // The author accepts no liability for any damage of buiness that this // product may cause // // Please use and enjoy. Please let me know of any bugs/mods/improvements // that you have found/implemented and I will fix/incorporate them into // this file #include "stdafx.h" #include "CurveWnd.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CCurveWnd CCurveWnd::CCurveWnd() { //Initialise members m_bTracking = false; m_nActiveKnot = - 1; m_iKnotRadius = 3; } CCurveWnd::~CCurveWnd() { } BEGIN_MESSAGE_MAP(CCurveWnd, CWnd) //{{AFX_MSG_MAP(CCurveWnd) ON_WM_CREATE() ON_WM_DESTROY() ON_WM_ERASEBKGND() ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_WM_RBUTTONUP() ON_WM_SETCURSOR() ON_WM_LBUTTONDBLCLK() ON_WM_RBUTTONDOWN() ON_WM_KEYDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CCurveWnd message handlers int CCurveWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CWnd::OnCreate(lpCreateStruct) == -1) return -1; //Create HitInfo Structure m_pHitInfo = new HITINFO; return 0; } void CCurveWnd::OnDestroy() { CWnd::OnDestroy(); //clean up delete m_pHitInfo; delete m_pCurve; } ///////////////////////////////////////////////////////////////////////////// // CCurveWnd initialisation // ///////////////////////////////////////////////////////////////////////////// BOOL CCurveWnd::Create(LPCTSTR lpszCurveName, const RECT &rect, CWnd* pWndParent, UINT nID, BOOL CreateCurveObj) { ASSERT(nID != NULL); ASSERT(pWndParent != NULL); //Create CurveWnd DWORD dwExStyle = WS_EX_CLIENTEDGE ; LPCTSTR lpszClassName = NULL; LPCTSTR lpszWindowName = lpszCurveName; DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER; const RECT& rc = rect; CWnd* pParentWnd = pWndParent; UINT nClientWndID = nID; LPVOID lpParam = NULL; BOOL b = CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rc, pParentWnd, nClientWndID, lpParam); if(CreateCurveObj) CreateCurveObject(lpszCurveName); return b; } void CCurveWnd::CreateCurveObject(CString strCurve) { //Create CurveObject Pointer CRect rcCurveWnd; GetClientRect(&rcCurveWnd); rcCurveWnd.DeflateRect(1,1); m_pCurve = new CCurveObject(); m_pCurve->CreateCurve(strCurve, rcCurveWnd); } ///////////////////////////////////////////////////////////////////////////// // CCurveWnd drawing // ///////////////////////////////////////////////////////////////////////////// BOOL CCurveWnd::OnEraseBkgnd(CDC* pDC) { return TRUE;} void CCurveWnd::OnPaint() { CPaintDC dc(this); // device context for painting CRect rcClient; GetClientRect(&rcClient); //Draw Window DrawWindow(&dc); } void CCurveWnd::DrawWindow(CDC* pDC) { CRect rcClient; GetClientRect(&rcClient); int cx = rcClient.Width(); int cy = rcClient.Height(); //Create Memory Device Context CDC MemDC; MemDC.CreateCompatibleDC(pDC); //Draw BackGround CBitmap bitmapBkGnd;; bitmapBkGnd.CreateCompatibleBitmap(pDC, cx, cy); CBitmap* pOldbitmapBkGnd = MemDC.SelectObject(&bitmapBkGnd); //Draw Grid DrawGrid(&MemDC); //Draw Knots and Curve if(AfxIsValidAddress(m_pCurve, sizeof(CObject))) { DrawCurve(&MemDC); DrawKnots(&MemDC); } pDC->BitBlt (0, 0, cx, cy, &MemDC, 0, 0, SRCCOPY); MemDC.SelectObject(pOldbitmapBkGnd); MemDC.DeleteDC(); } void CCurveWnd::DrawGrid(CDC* pDC) { CRect rcClipBox; pDC->GetClipBox(&rcClipBox); int cx = rcClipBox.Width(); int cy = rcClipBox.Height(); LOGBRUSH logBrush; logBrush.lbStyle = BS_SOLID; logBrush.lbColor = RGB(75, 75, 75); CPen pen; pen.CreatePen(PS_COSMETIC | PS_ALTERNATE, 1, &logBrush); CPen* pOldPen = pDC->SelectObject(&pen); //Draw Vertical Grid Lines for(int y = 1; y < 10; y++) { pDC->MoveTo(y*cx/10, cy); pDC->LineTo(y*cx/10, 0); } //Draw Horizontal Grid Lines for(int x = 1; x < 10; x++) { pDC->MoveTo(0, x*cy/10); pDC->LineTo(cx, x*cy/10); } pDC->SelectObject(pOldPen); } void CCurveWnd::DrawCurve(CDC* pDC) { CRect rcClipBox; pDC->GetClipBox(&rcClipBox); int cx = rcClipBox.Width(); int cy = rcClipBox.Height(); int iDrawX; //Draw Curve // create and select a thick, white pen CPen pen; pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); CPen* pOldPen = pDC->SelectObject(&pen); int iY; iY = m_pCurve->GetCurveY(1); //Get Starting point pDC->MoveTo(1, iY); for(int iX = 1; iX < cx; iX++) { int iYnext = m_pCurve->GetCurveY((float) iX / cx * m_pCurve->m_rcClipRect.right); iDrawX = (float) iYnext / m_pCurve->m_rcClipRect.bottom * cy; pDC->LineTo(CPoint(iX - 1, iDrawX)); iY = iDrawX; //Set next starting point } // Put back the old objects pDC->SelectObject(pOldPen); } void CCurveWnd::DrawKnots(CDC* pDC) { // create and select a thin, white pen CPen pen; pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); CPen* pOldPen = pDC->SelectObject(&pen); CRect rcClipBox; pDC->GetClipBox(&rcClipBox); int cx = rcClipBox.Width(); int cy = rcClipBox.Height(); //Draw Knots for(int pos = 1; pos < m_pCurve->GetKnotCount(); pos++) { // Create and select a solid white brush CKnot* pKnot = m_pCurve->GetKnot(pos); CKnot cTmpKnow; cTmpKnow.x = pKnot->x; cTmpKnow.y = pKnot->y; cTmpKnow.dwData = pKnot->dwData; //Set knot brush color int cyColor; pKnot->dwData ? cyColor = 255 : cyColor = 0; CBrush brush(RGB(cyColor,cyColor,cyColor)); CBrush* pOldBrush = pDC->SelectObject(&brush); cTmpKnow.x = (float) cTmpKnow.x / m_pCurve->m_rcClipRect.right * cx; cTmpKnow.y = (float) cTmpKnow.y / m_pCurve->m_rcClipRect.bottom * cy; //Draw knot CRect rc; rc.left = cTmpKnow.x - m_iKnotRadius; rc.right = cTmpKnow.x + m_iKnotRadius; rc.top = cTmpKnow.y - m_iKnotRadius; rc.bottom = cTmpKnow.y + m_iKnotRadius; pDC->Ellipse(&rc); pDC->SelectObject(pOldBrush); } // put back the old objects pDC->SelectObject(pOldPen); } ///////////////////////////////////////////////////////////////////////////// // CCurveWnd Message Handlers // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //Mouse Message Handlers void CCurveWnd::OnLButtonDown(UINT nFlags, CPoint point) { if(m_bTracking) return; SetFocus(); switch(m_pHitInfo->wHitCode) { case HTKNOT : { StartTracking(); SetActiveKnot(m_pHitInfo->nKnotIndex); } break; case HTCANVAS : { SetActiveKnot(-1); } default : break; //do nothing } CWnd::OnLButtonDown(nFlags, point); } void CCurveWnd::OnRButtonDown(UINT nFlags, CPoint point) { CWnd::OnRButtonDown(nFlags, point); } void CCurveWnd::OnLButtonDblClk(UINT nFlags, CPoint point) { RECT rc; GetClientRect(&rc); point.x = (float) point.x / rc.right * m_pCurve->m_rcClipRect.right; point.y = (float) point.y / rc.bottom * m_pCurve->m_rcClipRect.bottom; switch(m_pHitInfo->wHitCode) { case HTCURVE : { int iIndex = m_pCurve->InsertKnot(point); SetActiveKnot(iIndex); RedrawWindow(); } break; default : break;//do nothing } CWnd::OnLButtonDblClk(nFlags, point); } void CCurveWnd::OnMouseMove(UINT nFlags, CPoint point) { if(GetCapture() != this) StopTracking(); if(m_bTracking) TrackKnot(point); CWnd::OnMouseMove(nFlags, point); } void CCurveWnd::OnLButtonUp(UINT nFlags, CPoint point) { if(m_bTracking) StopTracking(); CWnd::OnLButtonUp(nFlags, point); } void CCurveWnd::OnRButtonUp(UINT nFlags, CPoint point) { CWnd::OnRButtonUp(nFlags, point); } ///////////////////////////////////////////////////////////////////////////// // CCurveWnd Functions // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //Active Knot Acces Functions void CCurveWnd::SetActiveKnot(UINT nIndex) { //Deactivate Old Knot if(m_nActiveKnot != - 1) { nIndex <= m_nActiveKnot ? m_nActiveKnot += 1 : m_nActiveKnot = m_nActiveKnot; CKnot* pKnotOld = m_pCurve->GetKnot(m_nActiveKnot); pKnotOld->SetData(TRUE); } //Activate New Knot if(nIndex != -1) { CKnot* pKnotNew = m_pCurve->GetKnot(nIndex); pKnotNew->SetData(FALSE); } m_nActiveKnot = nIndex; RedrawWindow(); } UINT CCurveWnd::GetActiveKnot() { return m_nActiveKnot;} ///////////////////////////////////////////////////////////////////////////// //Curve Object Functions void CCurveWnd::SetCurveObject(CCurveObject* pObject, BOOL bRedraw) { if(pObject != m_pCurve) { m_nActiveKnot = -1; //Reset active knot m_pCurve = pObject; //Set Curve Object pointer } if(bRedraw) RedrawWindow(); } CCurveObject* CCurveWnd::GetCurveObject(){ return m_pCurve;} ///////////////////////////////////////////////////////////////////////////// //Cursor Message Handlers BOOL CCurveWnd::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { BOOL b = FALSE; CPoint point; GetCursorPos(&point); ScreenToClient(&point); switch(HitTest(point)) { case HTCURVE : { HCURSOR hCursor; hCursor = AfxGetApp()->LoadCursor(IDC_ARRWHITE); SetCursor(hCursor); b = TRUE;; } break; case HTKNOT : { HCURSOR hCursor; hCursor = AfxGetApp()->LoadCursor(IDC_ARRBLCK); SetCursor(hCursor); b = TRUE; } break; default : //do nothing break; } if(!b) return CWnd::OnSetCursor(pWnd, nHitTest, message); else return TRUE; } ///////////////////////////////////////////////////////////////////////////// //Keyboard Message Handlers void CCurveWnd::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { BOOL bHandeldMsg = false; if(m_nActiveKnot != -1) { switch(nChar) { case VK_DELETE : { m_pCurve->RemoveKnot(m_nActiveKnot); m_nActiveKnot = -1; bHandeldMsg = true; } break; case VK_UP : { CKnot* pKnot = m_pCurve->GetKnot(m_nActiveKnot); CPoint pt; pKnot->GetPoint(&pt); pt.y -= 1; m_pCurve->MoveKnot(pt, m_nActiveKnot); bHandeldMsg = true; } break; case VK_DOWN : { CKnot* pKnot = m_pCurve->GetKnot(m_nActiveKnot); CPoint pt; pKnot->GetPoint(&pt); pt.y += 1; m_pCurve->MoveKnot(pt, m_nActiveKnot); bHandeldMsg = true; } break; case VK_LEFT : { CKnot* pKnot = m_pCurve->GetKnot(m_nActiveKnot); CPoint pt; pKnot->GetPoint(&pt); pt.x -= 1; m_pCurve->MoveKnot(pt, m_nActiveKnot); bHandeldMsg = true; } break; case VK_RIGHT : { CKnot* pKnot = m_pCurve->GetKnot(m_nActiveKnot); CPoint pt; pKnot->GetPoint(&pt); pt.x += 1; m_pCurve->MoveKnot(pt, m_nActiveKnot); bHandeldMsg = true; } break; default : break; //do nothing } RedrawWindow(); } if(!bHandeldMsg) CWnd::OnKeyDown(nChar, nRepCnt, nFlags); } ///////////////////////////////////////////////////////////////////////////// // CCurveWnd functions // ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // CCurveWnd implementation // ///////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //Hit Testing WORD CCurveWnd::HitTest(CPoint point) { RECT rc; GetClientRect(&rc); point.x = (float) point.x / rc.right * m_pCurve->m_rcClipRect.right; point.y = (float) point.y / rc.bottom * m_pCurve->m_rcClipRect.bottom; if(AfxIsValidAddress(m_pCurve, sizeof(CObject))) { m_pCurve->HitTest(point, m_pHitInfo); } else m_pHitInfo->wHitCode = HTCANVAS; return m_pHitInfo->wHitCode; } /////////////////////////////////////////////////////////////////////////////// //Tracking support void CCurveWnd::StartTracking() { m_bTracking = TRUE; SetCapture(); HCURSOR hCursor; hCursor = AfxGetApp()->LoadCursor(IDC_ARRBLCKCROSS); SetCursor(hCursor); } void CCurveWnd::TrackKnot(CPoint point) { RECT rc; GetClientRect(&rc); point.x = (float) point.x / rc.right * m_pCurve->m_rcClipRect.right; point.y = (float) point.y / rc.bottom * m_pCurve->m_rcClipRect.bottom; int iKnot = m_pHitInfo->nKnotIndex; //Index knot m_pCurve->MoveKnot(point, iKnot); RedrawWindow(); } void CCurveWnd::StopTracking() { if(!m_bTracking) return; m_bTracking = FALSE; ReleaseCapture(); } /////////////////////////////////////////////////////////////////////////////////// //Implementation