// 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();
//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, iYnext;
iY = m_pCurve->GetCurveY(1); //Get Starting point
pDC->MoveTo(1, iY);
for(int iX = 1; iX < cx; iX++)
{
int iYnext = m_pCurve->GetCurveY(iX);
pDC->LineTo(CPoint(iX - 1, iYnext));
iY = iYnext; //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);
//Draw Knots
for(int pos = 1; pos < m_pCurve->GetKnotCount(); pos++)
{
// Create and select a solid white brush
CKnot* pKnot = m_pCurve->GetKnot(pos);
//Set knot brush color
int cyColor;
pKnot->dwData ? cyColor = 255 : cyColor = 0;
CBrush brush(RGB(cyColor,cyColor,cyColor));
CBrush* pOldBrush = pDC->SelectObject(&brush);
//Draw knot
CRect rc;
rc.left = pKnot->x - m_iKnotRadius;
rc.right = pKnot->x + m_iKnotRadius;
rc.top = pKnot->y - m_iKnotRadius;
rc.bottom = pKnot->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)
{
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)
{
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)
{
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