Click here to Skip to main content
15,886,639 members
Articles / Desktop Programming / MFC

CCurveDlg - Curve Interpolation

Rate me:
Please Sign up or sign in to vote.
2.75/5 (4 votes)
15 Sep 20012 min read 113.5K   5.3K   42  
A Photoshop-like curve dialog.
// 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






By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions