Click here to Skip to main content
15,885,278 members
Articles / Programming Languages / C++

Mouse gestures recognition

Rate me:
Please Sign up or sign in to vote.
4.88/5 (57 votes)
22 Nov 20013 min read 334.7K   12K   193  
Feedforward multilayer neural network and mouse gesture recognition
/*
 Copyright (c) 2001 
 Author: Konstantin Boukreev 
 E-mail: konstantin@mail.primorye.ru 

 Created: 01.11.2001 17:39:39
 Version: 1.0.0

*/

/*
	bugs : sometimes, it doesn't draw anything in a top desk even if the pattern was recognized
*/

#include "stdafx.h"
#include "board.h"
#include "resource.h"
#include "GestureData.h"

const int DESK_WIDTH	= 100;
const int DESK_HEIGHT	= 100;
const int STATUS_HEIGHT	= 20;
const int STATUS_INDENT	= 2;

const double pi = 3.1415926535;

#ifdef max
#undef max
#endif

Board::Board(unsigned range) 
	: 
	NUMBER_OF_ANGLES(range),
	NUMBER_OF_ANCHOR_POINTS(range + 1),		
	m_angles (NUMBER_OF_ANGLES),
	m_cosines(NUMBER_OF_ANGLES),
	m_sinuses(NUMBER_OF_ANGLES),
	m_hpdesk(NUMBER_OF_ANGLES / 4),
	m_mode(untrained_mode)
{
	m_refresh	= false;
//	m_timestamp	= 0;	
	m_hMemDC	= 0;
	m_hBitmap	= 0;
	m_hOldBitmap = 0;
	m_hMemDC2	= 0;
	m_hBitmap2	= 0;
	m_hOldBitmap2 = 0;	
	m_hMemDC3	= 0;
	m_hBitmap3	= 0;
	m_hOldBitmap3 = 0;
	m_hMemDC4	= 0;
	m_hBitmap4	= 0;
	m_hOldBitmap4 = 0;
	
	m_hbrh1		= CreateSolidBrush(RGB(255, 255, 255));
	m_hbrh2		= CreateSolidBrush(RGB(231, 231, 231));
	m_hpen1		= CreatePen(PS_SOLID, 0, RGB(0, 0, 128));
	m_hpen2		= CreatePen(PS_SOLID, 0, RGB(192, 192, 192));
	m_hpen3		= CreatePen(PS_SOLID, 0, RGB(255, 0, 0));	
	m_hpen4		= CreatePen(PS_DOT, 0, RGB(221, 221, 221));	
	
	unsigned dc = 255 / m_hpdesk.size();
	for (unsigned i = 0; i < m_hpdesk.size(); ++i)
	{
		m_hpdesk[i] = CreatePen(PS_SOLID, 0, RGB(255 - i * dc, 0, i * dc));
	}
}

Board::~Board() 
{
	CleanupMemDC();

	if (m_hbrh1) DeleteObject(m_hbrh1);
	if (m_hbrh2) DeleteObject(m_hbrh2);
	if (m_hpen1) DeleteObject(m_hpen1);
	if (m_hpen2) DeleteObject(m_hpen2);
	if (m_hpen3) DeleteObject(m_hpen3);
	if (m_hpen4) DeleteObject(m_hpen4);

	for (unsigned i = 0; i < m_hpdesk.size(); ++i)
		if (m_hpdesk[i]) DeleteObject(m_hpdesk[i]);
}

LRESULT Board::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	RECT rc;
	GetClientRect(&rc);

	PAINTSTRUCT ps;		
	HDC hDC = BeginPaint(&ps);
	Draw(hDC, rc);
	EndPaint(&ps);

	return 0;
}

LRESULT Board::OnEraseBknd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	return 1;
}

LRESULT Board::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	CleanupMemDC();
	return 0;
}

LRESULT Board::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	if (wParam & MK_RBUTTON)
	{
		POINT pt = {LOWORD(lParam), HIWORD(lParam)};
		AddMouseRecord(pt);
	}
	return 0;
}

LRESULT Board::OnRButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{	
	// at first always tries to stop any active action (training or testing)
	CWindow(GetParent()).SendMessage(WM_COMMAND, MAKEWPARAM(ID_STOP, 0));
	StartMouseRecord();
	return 0;
}

LRESULT Board::OnRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	StopMouseRecord();	
	return 0;
}

LRESULT Board::OnAsyncRefresh(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	MSG msg; 	
	while (PeekMessage(&msg, m_hWnd, WM_ASYNC_REFRESH, WM_ASYNC_REFRESH, PM_REMOVE))
		;
	Refresh();
	return 0;
}

void Board::Draw(HDC hDC, RECT& rc)
{
	if (!m_hMemDC) 
		CreateMemDC(hDC, rc);
	
	if (m_refresh) 
	{
		DrawMemDC();
		m_refresh = false;
	}			

	BitBlt(hDC, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 
		m_hMemDC, 0, 0, SRCCOPY);
	
//	m_timestamp = GetTickCount();
}

void Board::CreateMemDC(HDC hDC, RECT& rc)
{
	CleanupMemDC();		
	
	m_hMemDC	= CreateCompatibleDC(hDC);
	m_hBitmap	= CreateCompatibleBitmap(hDC, rc.right - rc.left, rc.bottom - rc.top);
	m_hOldBitmap = (HBITMAP)SelectObject(m_hMemDC, m_hBitmap);

	m_hMemDC2	= CreateCompatibleDC(hDC);
	m_hBitmap2	= CreateCompatibleBitmap(hDC, DESK_WIDTH, DESK_HEIGHT);
	m_hOldBitmap2 = (HBITMAP)SelectObject(m_hMemDC2, m_hBitmap2);

	m_hMemDC3	= CreateCompatibleDC(hDC);
	m_hBitmap3	= CreateCompatibleBitmap(hDC, rc.right - rc.left - STATUS_INDENT * 2, STATUS_HEIGHT);
	m_hOldBitmap3 = (HBITMAP)SelectObject(m_hMemDC3, m_hBitmap3);

	m_hMemDC4	= CreateCompatibleDC(hDC);
	m_hBitmap4	= CreateCompatibleBitmap(hDC, DESK_WIDTH, DESK_HEIGHT);
	m_hOldBitmap4 = (HBITMAP)SelectObject(m_hMemDC4, m_hBitmap4);

	m_refresh	= true;
	m_rcMemDC	= rc;
}

void Board::CleanupMemDC()
{
	if (m_hMemDC)
	{
		SelectObject(m_hMemDC, m_hOldBitmap);
		DeleteDC(m_hMemDC);
		DeleteObject(m_hBitmap);
		
		SelectObject(m_hMemDC2, m_hOldBitmap2);
		DeleteDC(m_hMemDC2);
		DeleteObject(m_hBitmap2);

		SelectObject(m_hMemDC3, m_hOldBitmap3);
		DeleteDC(m_hMemDC3);
		DeleteObject(m_hBitmap3);

		SelectObject(m_hMemDC4, m_hOldBitmap4);
		DeleteDC(m_hMemDC4);
		DeleteObject(m_hBitmap4);

		m_hMemDC = 0;
	}
}

void Board::DrawMemDC()
{
	unsigned cx = m_rcMemDC.right - m_rcMemDC.left;
	unsigned cy = m_rcMemDC.bottom - m_rcMemDC.top;

	int n_save_dc = SaveDC(m_hMemDC);

	FillRect(m_hMemDC, &m_rcMemDC, m_hbrh1);

	// draw grid

	SelectObject(m_hMemDC, m_hpen4);

	#define GRID_WIDTH	16
	#define GRID_HEIGHT 16

	for (int x = m_rcMemDC.left; x < m_rcMemDC.right; x += GRID_WIDTH)
	{
		MoveToEx(m_hMemDC, x, m_rcMemDC.top, 0);
		LineTo  (m_hMemDC, x, m_rcMemDC.bottom);
	}
		
	for (int y = m_rcMemDC.top; y < m_rcMemDC.bottom; y += GRID_HEIGHT)
	{
		MoveToEx(m_hMemDC, m_rcMemDC.left, y, 0);
		LineTo  (m_hMemDC, m_rcMemDC.right, y);
	}
		
	// draw main screen

	if (training_mode == m_mode || 
		trained_mode == m_mode)
	{
		RECT rc = m_rcMemDC;
		rc.top  += 3;
		rc.left += 3;
		rc.right  -= (DESK_WIDTH + 3);
		rc.bottom -= (STATUS_HEIGHT + 3);
		DrawGraph(m_hMemDC, rc);	
	}			
	else
	{
		DrawPath(m_hMemDC, m_rcMemDC);	

		if (recognized_success	== m_mode || 
			recognized_fail		== m_mode)
		{
			DrawWinners(m_hMemDC, m_rcMemDC);
		}		
	}
		
	// draw desks and status info

	int x_desk = m_rcMemDC.right - DESK_WIDTH - 3;
	int y_desk = m_rcMemDC.top   + 3;		
	RECT rcDesc = {0, 0, DESK_WIDTH, DESK_HEIGHT};

	DrawTopDesc(m_hMemDC2, rcDesc);
	BLENDFUNCTION bf = {AC_SRC_OVER, 0, 150, 0};

	AlphaBlend(m_hMemDC, x_desk, y_desk, DESK_WIDTH, DESK_HEIGHT, 
		m_hMemDC2, 0, 0, DESK_WIDTH, DESK_HEIGHT, bf);

	y_desk += (DESK_HEIGHT + 3);

	DrawBottomDesc(m_hMemDC2, rcDesc);
	bf.SourceConstantAlpha = 170; 
	AlphaBlend(m_hMemDC, x_desk, y_desk, DESK_WIDTH, DESK_HEIGHT, 
		m_hMemDC2, 0, 0, DESK_WIDTH, DESK_HEIGHT, bf);
	bf.SourceConstantAlpha = 150;

	RECT rcStatus = {0, 0, cx - STATUS_INDENT * 2, STATUS_HEIGHT};
	DrawStatusInfo(m_hMemDC3, rcStatus);
	AlphaBlend(m_hMemDC, STATUS_INDENT, cy - STATUS_HEIGHT, cx - STATUS_INDENT * 2, STATUS_HEIGHT, 
		m_hMemDC3, 0, 0, cx - STATUS_INDENT * 2, STATUS_HEIGHT, bf);

	RestoreDC(m_hMemDC, n_save_dc);
}

void Board::DrawWinners(HDC hDC, RECT& rc)
{
	int n_save_dc = SaveDC(hDC);

	SetBkMode   (hDC, TRANSPARENT);	
	SetTextColor(hDC, RGB(191, 191, 191));
	SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));

	char buf[255] = {0};
	SIZE sz;
	int x = rc.left + 3; 
	int y = rc.top  + 3;
	for (unsigned n = 0; n < sizeof(m_winners)/sizeof(m_winners[0]); ++n)
	{	
		sprintf(buf, "%s - %f", pattern_names[m_winners[n].m_id], m_winners[n].m_probability);
		TextOut(hDC, x, y, buf, lstrlen(buf));	
		GetTextExtentPoint32(hDC, buf, lstrlen(buf), &sz);
		y += sz.cy;
	}

	RestoreDC(hDC, n_save_dc);
}

void Board::DrawPath(HDC hDC, RECT& rc)
{
	typedef path_t::iterator iterator;
	iterator i;

	// draw a mouse's path

	for (i = m_path.begin(); i != m_path.end(); ++i)
	{
		POINT& pt = (*i);
		SetPixel(hDC, pt.x, pt.y, RGB(255, 0, 0));	
	}		

	_ASSERTE((m_path2.size() == NUMBER_OF_ANCHOR_POINTS) || (m_path2.size() == 0));

	// draw a smoothed path

	for (i = m_path2.begin(); i != m_path2.end(); ++i)
	{
		POINT& pt = (*i);

		SetPixel(hDC, pt.x - 1, pt.y + 0, RGB(0, 0, 255));
		SetPixel(hDC, pt.x + 1, pt.y + 0, RGB(0, 0, 255));
		SetPixel(hDC, pt.x + 0, pt.y + 1, RGB(0, 0, 255));
		SetPixel(hDC, pt.x + 0, pt.y - 1, RGB(0, 0, 255));
		SetPixel(hDC, pt.x + 0, pt.y + 0, RGB(0, 0, 255));
	}		
}

void Board::DrawGraph(HDC hDC, RECT& rc)
{
	int n_save_dc = SaveDC(hDC);

	SelectObject(hDC, m_hpen3);	

	typedef vector_real_t::iterator iterator;
	iterator i;

	int x = rc.left;		
	double ratio = ((double)rc.bottom - rc.top) / m_max_error;
	
	for (i = m_errors.begin(); i != m_errors.end(); ++i)
	{
		MoveToEx(hDC, x, rc.bottom, 0);
		LineTo(hDC, x, rc.bottom - int((*i) * ratio));
		++x;
	}		

	RestoreDC(hDC, n_save_dc);
}

void Board::DrawTopDesc(HDC hDC, RECT& rc)
{
	int n_save_dc = SaveDC(hDC);

	SelectObject(hDC, m_hbrh2);
	SelectObject(hDC, m_hpen2);
	Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);

	if ((recognizing_mode	== m_mode && m_path2.size()) ||
		(recognized_success	== m_mode && m_path2.size()) ||
		(recognized_fail	== m_mode && m_path2.size()) ||
		(training_mode		== m_mode && m_update_top_desk))
	{
		SelectObject(hDC, m_hpen3);
		DrawVector(hDC, rc, m_angles);
	}
	
	RestoreDC(hDC, n_save_dc);
}

void Board::DrawBottomDesc(HDC hDC, RECT& rc)
{		
	if ((training_mode == m_mode || trained_mode == m_mode)
		&& m_update_bottom_desk)
	{
		BitBlt(hDC, 0, 0, rc.right - rc.left, rc.bottom - rc.top, m_hMemDC4, 0, 0, SRCCOPY);
	}
	else
	{
		int n_save_dc = SaveDC(hDC);
		SelectObject(hDC, m_hbrh2);
		SelectObject(hDC, m_hpen2);
		Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);

		if (recognized_success == m_mode)
		{
			//  draw a recognized pattern

			vector_real_t vec(NUMBER_OF_ANGLES);			
			for (unsigned n = 0; n < vec.size(); ++n)
				vec[n] = pattern_data[m_winners[0].m_id][n];

			SelectObject(hDC, m_hpen3);
			DrawVector(hDC, rc, vec);
		}

		RestoreDC(hDC, n_save_dc);
	}
}

void Board::DrawStatusInfo(HDC hDC, RECT& rc)
{
	int n_save_dc = SaveDC(hDC);

	SelectObject(hDC, m_hbrh2);
	SelectObject(hDC, m_hpen2);
	Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);

	COLORREF clrTxt = RGB(0, 0, 0);

	TCHAR buf[512] = {0};

	if		(recognizing_mode == m_mode)
		sprintf(buf, "points %u", m_path.size());
	else if (training_mode == m_mode && m_errors.size())
		sprintf(buf, "error: %f", m_errors.back());	
	else if (trained_mode == m_mode && m_errors.size())
		sprintf(buf, "training cycles: %u, mean square error: %f", m_train_cycles, m_errors.back());	
	else if (recognized_success == m_mode)
	{
		clrTxt	= RGB(0, 0, 255);	
		sprintf(buf, "recognition is successful: %s (%f)", 
			pattern_names[m_winners[0].m_id], m_winners[0].m_probability);
	}
	else if (recognized_fail == m_mode)
	{
		clrTxt	= RGB(255, 0, 0);
		sprintf(buf, "unable to recognize anything, the best is %s (%f), points %u", 
			pattern_names[m_winners[0].m_id], m_winners[0].m_probability, m_path.size());
	}
	else 
		sprintf(buf, "untrained net");

	if (m_info.size())
	{
		_tcscat (buf, _T(" - "));
		_tcsncat(buf, m_info.c_str(), sizeof(buf)/sizeof(buf[0]) - _tcslen(buf));
	}
				
	SetBkMode(hDC, TRANSPARENT);	
	SetTextColor(hDC, clrTxt);
	SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT));
	SIZE sz;
	GetTextExtentPoint32(hDC, buf, lstrlen(buf), &sz);
	int x = rc.left + 3; 
	int y = rc.top + ((rc.bottom - rc.top) - sz.cy) / 2;
	ExtTextOut(hDC, x, y, ETO_CLIPPED, &rc, buf, lstrlen(buf), 0);	

	RestoreDC(hDC, n_save_dc);
}

void Board::DrawVector(HDC hDC, RECT& rc, vector_real_t& vec)
{
	typedef path_t::iterator iterator;

	path_t path;
	vector_to_path(rc, vec, path);

	iterator i = path.begin();
	MoveToEx(hDC, (*i).x, (*i).y, 0);
	++i;

	unsigned n = 0;
	for (; i != path.end(); ++i, ++n)
	{
		SelectObject(hDC, m_hpdesk[n / 4]);
		LineTo  (hDC, (*i).x, (*i).y);
		SetPixel(hDC, (*i).x, (*i).y, RGB(0, 0, 255));	
	}	
}

/////////////////////////////////////////////////

bool Board::TransfromPath()
{
	typedef path_t::iterator iterator;

	m_path2.assign(m_path.begin(), m_path.end());
		
	if (NUMBER_OF_ANCHOR_POINTS > m_path.size())
	{
		// todo : stretch path or warning message;		
		//	m_path.clear();
		m_path2.clear();
		m_mode = recognizing_mode;
		return false;
	}	
	else if (NUMBER_OF_ANCHOR_POINTS < m_path.size())
	{
		// smooth path		
		// finds smallest interval and replaces two points on median point

		while (m_path2.size() > NUMBER_OF_ANCHOR_POINTS)
		{
			double d;
			double d_min = std::numeric_limits<double>::max();
			
			iterator p_min  = m_path2.begin();
			++p_min;

			iterator p		= p_min;
			iterator i		= p_min;
			++i;

			iterator last	= m_path2.end();
			--last;

			for (; i != last; ++i)
			{
				d = sqrt(pow((*p).x - (*i).x, 2) + pow((*p).y - (*i).y, 2));
				if (d < d_min)
				{
					d_min = d;
					p_min = p;
				}
				p = i;
			}

			p = p_min;
			i = ++p_min;

			POINT pt = {((*p).x + (*i).x) / 2, ((*p).y + (*i).y) / 2};
			*i = pt;				// changes coord of a base point
			m_path2.erase(p);		// erases an odd point 
		}
	}		
	else
	{
		// nothing		
	}		
	
	_ASSERTE(m_path2.size() == NUMBER_OF_ANCHOR_POINTS);

	// computes angles, cosines and sines

	iterator i = m_path2.begin();
	iterator p = i++;
	unsigned n = 0;

	for (; i != m_path2.end(); ++i, ++n)
	{
		POINT pt2 = (*i);		
		POINT pt1 = (*p);		

		pt2.x -= pt1.x;
		pt2.y -= pt1.y;

		if (pt2.x || pt2.y)
		{
			m_cosines[n] = float(pt2.y / sqrt(pt2.x * pt2.x + pt2.y * pt2.y));
			m_sinuses[n] = (float)sqrt(1. - m_cosines[n] * m_cosines[n]);		
			if (pt2.x < 0) m_sinuses[n] = - m_sinuses[n];		
			m_angles[n] = float(acos(m_cosines[n]) * 180. / pi);
			if (pt2.x < 0) m_angles[n] = 360.f - m_angles[n];
		}
		else
		{
			m_cosines[n] = 1;
			m_sinuses[n] = 0;
			m_angles[n]  = 0;
		}
		
		p = i;

		_ASSERTE(m_cosines[n] <=  1.);
		_ASSERTE(m_cosines[n] >= -1.);
		_ASSERTE(m_sinuses[n] <=  1.);
		_ASSERTE(m_sinuses[n] >= -1.);
		_ASSERTE(m_angles [n] <= 360.);
		_ASSERTE(m_angles [n] >= 0.);		
	}	

	return true;
}

///////////////////////////////////////////////////////////

unsigned Board::StartTraining()
{
	m_mode = training_mode;
	m_update_top_desk = false;
	m_update_bottom_desk = false;
	m_path.clear();
	m_path2.clear();
	m_max_error = 0;	
	m_errors.clear();
	m_errors.reserve(m_rcMemDC.right - m_rcMemDC.left - DESK_WIDTH - 6);	
//	Refresh();
	return m_rcMemDC.right - m_rcMemDC.left - DESK_WIDTH - 6;
}

void Board::UpdateTrainingError(float current_error)
{
	m_max_error = std::_MAX(m_max_error, current_error);
	m_errors.push_back(current_error);
//	Refresh();
}

void Board::UpdateTrainingVector(vector_real_t& vec)
{
	_ASSERTE(vec.size() == m_angles.size());

	typedef vector_real_t::iterator iterator;
	for (iterator to = m_angles.begin(), from = vec.begin(); to != m_angles.end(); ++to, ++from)
	{
		*to = *from;
	}
	m_update_top_desk = true;
}

void Board::UpdateTrainingWeight(vector_real_t::iterator w_begin, vector_real_t::iterator w_end)
{
	_ASSERTE((w_end - w_begin) <= DESK_WIDTH * DESK_HEIGHT);

	typedef vector_real_t::iterator iterator;

	int n_save_dc = SaveDC(m_hMemDC4);

	RECT rc = {0, 0, DESK_WIDTH, DESK_HEIGHT};
	
	#define SPACE 2

	SelectObject(m_hMemDC4, m_hbrh2);
	SelectObject(m_hMemDC4, m_hpen2);
	Rectangle(m_hMemDC4, rc.left, rc.top, rc.right, rc.bottom);

	unsigned dim = (unsigned)sqrt(w_end - w_begin);

	unsigned left = (DESK_WIDTH - dim * SPACE) / 2;
	unsigned top  = (DESK_HEIGHT - dim * SPACE) / 2;
		
	iterator i = w_begin;
	for (unsigned x = left; x < left + dim * SPACE; x += SPACE)
		for (unsigned y = top; y < top + dim * SPACE; y += SPACE)
		{
			float f = *i;			
			COLORREF clr;
			if (fabs(f) > 1.) 
				clr = f < 0. ? RGB(255, 255, 0) : RGB(0, 0, 255) ;			
			else 
				clr = f < 0. ? RGB(f * 255., 0, 0) : RGB(0, f * 255., 0);

			SetPixel(m_hMemDC4, x, y, clr);

			SetPixel(m_hMemDC4, x + 0, y + 1, clr);
			SetPixel(m_hMemDC4, x + 1, y + 0, clr);
			SetPixel(m_hMemDC4, x + 1, y + 1, clr);

			++i;
		}

	_ASSERTE(i <= w_end);
	RestoreDC(m_hMemDC4, n_save_dc);
	m_update_bottom_desk = true;
}

void Board::StopTraining(unsigned last_cycle)
{
//	m_mode = recognizing_mode;
//	m_errors.clear();
	m_mode = trained_mode;
	m_train_cycles = last_cycle + 1; // 0..last_cycle
}

void Board::StartMouseRecord()
{	
//	CWindow(GetParent()).SendMessage(WM_COMMAND, MAKEWPARAM(ID_STOP, 0));
	m_path.clear();
	m_path2.clear();
	if (untrained_mode != m_mode)
		m_mode = recognizing_mode;
	Refresh();
}

void Board::AddMouseRecord(POINT pt)
{	
	m_path.push_back(pt);
	Refresh();
}

void Board::StopMouseRecord()
{	
	bool r = TransfromPath();	
	if (untrained_mode != m_mode && r)
	{		
		CWindow(GetParent()).SendMessage(WM_COMMAND, MAKEWPARAM(ID_RECOGNIZE, 0));
	}	
	Refresh();
}

///////////////////////////////////////

// static 
Board::vector_to_path(RECT& rc, vector_real_t& vec, path_t& path)
{
	typedef vector_real_t::iterator iterator;

	int y = 0, x = 0, cx = rc.right - rc.left, cy = rc.bottom - rc.top;				
	RECT borders = {0};
	iterator i;

	double dx = (double)cx / (vec.size() - 1);
	double dy = (double)cy / (vec.size() - 1);

	for (i = vec.begin(); i != vec.end(); ++i)
	{
		double angle = (double)*i;

		x += int(dx * sin(angle * (pi / 180.)));
		y += int(dy * cos(angle * (pi / 180.)));
		
		if (borders.left   > x) borders.left   = x;
		if (borders.right  < x) borders.right  = x;
		if (borders.top    > y) borders.top    = y;
		if (borders.bottom < y) borders.bottom = y;
	}

	x = - borders.left + (cx - (borders.right - borders.left)) / 2;
	y = - borders.top  + (cy - (borders.bottom - borders.top)) / 2; 

	POINT pt = {x, y};
	path.push_back(pt);

	unsigned n = 0;
	for (i = vec.begin(); i != vec.end(); ++i, ++n)
	{
		double angle = (double)*i;
		x += int(dx * sin(angle * (pi / 180.)));
		y += int(dy * cos(angle * (pi / 180.)));
		pt.x = x; pt.y = y;
		path.push_back(pt);
	}	
}

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
I am freelance programmer. About 3 years of experience in C++ and I would rather use ATL, STL, WTL but not MFC Smile | :) . Main backgrounds are Win32 API, COM and Networking. Now I am interested about AI (Neural Network, Fuzzy Logic and GA). Currently based in Vladivostok, Russia.

Comments and Discussions