Click here to Skip to main content
15,896,726 members
Articles / Desktop Programming / MFC

2D LUA Based Robot Simulator

Rate me:
Please Sign up or sign in to vote.
4.89/5 (26 votes)
14 Apr 2014Public Domain9 min read 131.2K   7.9K   119  
An article on designing your own robot simulator
// Author : Auralius Manurung
// Contact : auralius@lavabit.com

#include "StdAfx.h"
#include "Canvas.h"
#include <windows.h>

#include <iostream>
#include <fstream>


#define round(a) ( int ) ( a + .5 )

CCanvas::CCanvas(void)
{
	// Init
	m_isSelecting = false;
	m_isResizing = false;
	m_isMoving = true;
	m_isSnapToGrid = false;
	m_isSelectAll = false;
	m_cursorType = TOOL_SELECT;

	strcpy(m_message,"\n");
}

CCanvas::~CCanvas(void)
{
	// Let's clean up !!!
	while(!m_object.empty()){
		free(m_object.front());
		m_object.pop_front();
	}
}

void CCanvas::drawGrid(CDC *dc, HWND hwnd, int gridsizeX, int gridsizeY)
{
	// Adapted from Johan Rosengren, Abstrakt Mekanik AB
	CRect rect;
	GetClientRect(hwnd, &rect);

	int width = rect.Width();
	int height = rect.Height();
	int stepx = rect.right / gridsizeX;
	int stepy = rect.bottom / gridsizeY;

	CPen pen;
	pen.CreatePen(PS_DOT, 1, RGB(127,127,127));
	CPen *oldPen = dc->SelectObject(&pen);

	// Draw grid - x axis
	for( int x = 0 ; x <= stepx ; x++ )
	{
		int axisstep = round( static_cast< float >(gridsizeX * x ));

		// Draw axis label
		char buf[5];
		itoa(x*50, buf, 10);
		CRect textrect(axisstep, 0, axisstep + 30, 20 );
		SetTextColor(*dc, RGB(0,0,127));
		dc->DrawText(buf, &textrect, 0);

		dc->MoveTo(axisstep, 0);
		dc->LineTo( axisstep, height );
	}

	// Draw grid - y axis
	for( int y = 0; y <= stepy ; y++ )
	{
		dc->MoveTo( 0, round( static_cast< float >( gridsizeY* y ) ) );
		dc->LineTo( width, round( static_cast< float >( gridsizeY * y ) ) );
	}

	dc->SelectObject(oldPen); // recover pen
}

void CCanvas::drawHitMark(CDC *dc, int flag)
{	
	//In red :D
	CPen sensorPen(PS_SOLID, 5, RGB(255,0,0));
	CPen *oldPen = dc->SelectObject(&sensorPen);

	if (flag == 0) // for rectangle and ellipse
	{
		dc->Rectangle(m_topLCorner.x, m_topLCorner.y, m_topLCorner.x + 6, m_topLCorner.y + 6);				// top left corner
		dc->Rectangle(m_topRCorner.x - 6, m_topRCorner.y + 6, m_topRCorner.x, m_topRCorner.y);				// top right
		dc->Rectangle(m_bottomLCorner.x, m_bottomLCorner.y, m_bottomLCorner.x + 6, m_bottomLCorner.y - 6);	// bottom left
		dc->Rectangle(m_bottomRCorner.x - 6, m_bottomRCorner.y - 6, m_bottomRCorner.x, m_bottomRCorner.y);	// bottom right
		dc->Rectangle(m_centerL.x, m_centerL.y - 3, m_centerL.x + 6, m_centerL.y + 3);						// center left
		dc->Rectangle(m_centerR.x - 6, m_centerR.y - 3, m_centerR.x, m_centerR.y + 3);						// center right
		dc->Rectangle(m_centerT.x - 3, m_centerT.y, m_centerT.x + 3, m_centerT.y + 6);						// center top
		dc->Rectangle(m_centerB.x - 3, m_centerB.y - 6, m_centerB.x + 3, m_centerB.y);						// center bottom
	}
	else if (flag == 1) // for arrow
	{
		dc->Ellipse(m_topLCorner.x - 3, m_topLCorner.y - 3, m_topLCorner.x + 3, m_topLCorner.y + 3);		// arrow use 2 point only
		dc->Ellipse(m_bottomRCorner.x - 3, m_bottomRCorner.y - 3, m_bottomRCorner.x + 3, m_bottomRCorner.y + 3);	 
	}

	dc->SelectObject(oldPen);
}

bool CCanvas::isTooSmall()
{
	if (abs(m_ptEnd.x - m_ptStart.x) < 5 && abs(m_ptEnd.y - m_ptStart.y) < 5)
		return true;
	else
		return false;
}

void CCanvas::setResize()
{
	if (m_isSelecting == true && m_isMoving == false)
	{
		// Left & right center
		if (m_ptEnd.x >= (m_centerL.x - m_gridsizeX) && m_ptEnd.x <= (m_centerL.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_centerL.y - m_gridsizeY) && m_ptEnd.y <= (m_centerL.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZEWE));
			m_isResizing = true;
			m_resizeDirection = LEFT_CENTER;
		}

		else if (m_ptEnd.x >= (m_centerR.x - m_gridsizeX) && m_ptEnd.x <= (m_centerR.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_centerR.y - m_gridsizeY) && m_ptEnd.y <= (m_centerR.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZEWE));
			m_isResizing = true;
			m_resizeDirection = RIGHT_CENTER;
		}

		// Top  & bottom center
		else if (m_ptEnd.x >= (m_centerT.x - m_gridsizeX) && m_ptEnd.x <= (m_centerT.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_centerT.y - m_gridsizeY) && m_ptEnd.y <= (m_centerT.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENS));
			m_isResizing = true;
			m_resizeDirection = TOP_CENTER;
		}

		else if (m_ptEnd.x >= (m_centerB.x - m_gridsizeX) && m_ptEnd.x <= (m_centerB.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_centerB.y - m_gridsizeY) && m_ptEnd.y <= (m_centerB.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENS));
			m_isResizing = true;
			m_resizeDirection = BOTTOM_CENTER;
		}

		// Top left corner & bottom right corner
		else if (m_ptEnd.x >= (m_topLCorner.x - m_gridsizeX) && m_ptEnd.x <= (m_topLCorner.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_topLCorner.y - m_gridsizeY) && m_ptEnd.y <= (m_topLCorner.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENWSE));
			m_isResizing = true;
			m_resizeDirection = TOP_LEFT_CORNER;
		}

		else if	(m_ptEnd.x >= (m_bottomRCorner.x - m_gridsizeX) && m_ptEnd.x <= (m_bottomRCorner.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_bottomRCorner.y - m_gridsizeY) && m_ptEnd.y <= (m_bottomRCorner.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENWSE));
			m_isResizing = true;
			m_resizeDirection = BOTTOM_RIGHT_CORNER;
		}

		// Top right corner & bottom left corner
		else if (m_ptEnd.x >= (m_topRCorner.x - m_gridsizeX) && m_ptEnd.x <= (m_topRCorner.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_topRCorner.y - m_gridsizeY) && m_ptEnd.y <= (m_topRCorner.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENESW));
			m_isResizing = true;
			m_resizeDirection = TOP_RIGHT_CORNER;
		}

		else if (m_ptEnd.x >= (m_bottomLCorner.x - m_gridsizeX) && m_ptEnd.x <= (m_bottomLCorner.x + m_gridsizeX) && 
			m_ptEnd.y >= (m_bottomLCorner.y - m_gridsizeY) && m_ptEnd.y <= (m_bottomLCorner.y + m_gridsizeY))
		{
			::SetCursor(::LoadCursor(NULL, IDC_SIZENESW));
			m_isResizing = true;
			m_resizeDirection = BOTTOM_LEFT_CORNER;
		}

		// Default
		else
			::SetCursor(::LoadCursor(NULL, IDC_ARROW));
	}
}

void CCanvas::resizeObject()
{
	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++)
	{
		OBJECT *obj = *m_iterator;
		if (obj->isSelected == true)
		{
			if (obj->objectName == TOOL_RECTANGLE  || obj->objectName == TOOL_CIRCLE || obj->objectName == TOOL_LINE)
			{
				int h = obj->point[1].x - obj->point[0].x;
				int w = obj->point[1].y - obj->point[0].y;
				
				if (m_resizeDirection == TOP_LEFT_CORNER)
				{

					obj->point[0].x = obj->point[0].x + m_ptEnd.x - m_ptEnd0.x;
					obj->point[0].y = obj->point[0].y + m_ptEnd.y - m_ptEnd0.y;
				}
				else if (m_resizeDirection == TOP_RIGHT_CORNER)
				{
					obj->point[1].x = obj->point[0].x + h + m_ptEnd.x - m_ptEnd0.x;
					obj->point[0].y = obj->point[0].y + m_ptEnd.y - m_ptEnd0.y;
				}
				else if (m_resizeDirection == BOTTOM_LEFT_CORNER)
				{
					obj->point[1].y = obj->point[0].y + w + m_ptEnd.y - m_ptEnd0.y;
					obj->point[0].x = obj->point[0].x + m_ptEnd.x - m_ptEnd0.x;
				}
				else if (m_resizeDirection == BOTTOM_RIGHT_CORNER)
				{
					obj->point[1].x = obj->point[0].x + h + m_ptEnd.x - m_ptEnd0.x;
					obj->point[1].y = obj->point[0].y + w + m_ptEnd.y - m_ptEnd0.y;
				}
				else if (m_resizeDirection == RIGHT_CENTER)
				{
					obj->point[1].x = obj->point[0].x + h + m_ptEnd.x - m_ptEnd0.x;
				}
				else if (m_resizeDirection == LEFT_CENTER)
				{
					obj->point[0].x = obj->point[0].x + m_ptEnd.x - m_ptEnd0.x;
				}
				else if (m_resizeDirection == TOP_CENTER)
				{
					obj->point[0].y = obj->point[0].y + m_ptEnd.y - m_ptEnd0.y;
				}
				else if (m_resizeDirection == BOTTOM_CENTER)
				{
					obj->point[1].y = obj->point[0].y + w + m_ptEnd.y - m_ptEnd0.y;
				}
			}
		}
	}
	//m_isResizing = false;
}

void CCanvas::swapLong(LONG *a, LONG *b)
{
	LONG buf;
	buf = *a;
	*a = *b;
	*b = buf;
}

bool CCanvas::isAnObjectSelected(CPoint pt)
{
	if (m_object.empty())
		return false;

	if (m_isMoving || m_isSelectAll) // moving something means selecting something
		return true;

	if (!m_isResizing)
		deselectObjects();

	for (std::list<OBJECT *>::reverse_iterator reverseIterator = m_object.rbegin(); reverseIterator != m_object.rend(); ++reverseIterator)
	{
		OBJECT *obj = *reverseIterator;		

		if (obj->objectName == TOOL_RECTANGLE || obj->objectName == TOOL_CIRCLE)
		{
			// We have four quadrants! Be careful !!!
			if (obj->point[0].x > obj->point[1].x)
				swapLong(&obj->point[0].x, &obj->point[1].x);
			if (obj->point[0].y > obj->point[1].y)
				swapLong(&obj->point[0].y, &obj->point[1].y);

			if( pt.x >= obj->point[0].x && pt.x <= obj->point[1].x
				&& pt.y >= obj->point[0].y && pt.y <= obj->point[1].y )
			{
				obj->isSelected = true;
				m_isSelecting = true;
				if (m_cursorType != TOOL_DELETE)
					m_cursorType = TOOL_SELECT;
				return true;
			}
		}

		else if (obj->objectName == TOOL_LINE)
		{
			bool checkX, checkY;
			float a = obj->point[1].y - obj->point[0].y; // a = y2 - y1
			float b = obj->point[1].x - obj->point[0].x; // b = x2 - x1

			// watcn for vertical lines, it will give b = 0!
			if (abs(b) < 20){
				checkY = pt.y <= max(obj->point[0].y, obj->point[1].y) + 10 && pt.y >= min (obj->point[0].y, obj->point[1].y) - 10;
				checkX = pt.x <= max(obj->point[0].x, obj->point[1].x) + 10 && pt.x >= min (obj->point[0].x, obj->point[1].x) - 10; 
			}

			else{
				float y = a / b * (pt.x - obj->point[0].x) + obj->point[0].y;
				checkX = pt.x <= max(obj->point[0].x, obj->point[1].x) + 2 && pt.x >= min (obj->point[0].x, obj->point[1].x) - 2;
				checkY = pt.y >= y - 10 && pt.y <= y + 10;
			}
		
			//if (pt.y >= y - 10 && pt.y <= y + 10 && pt.x <= max(obj->point[0].x, obj->point[1].x) + 2 && pt.x >= min (obj->point[0].x, obj->point[1].x) - 2)
			if (checkX && checkY)
			{
				obj->isSelected = true;
				m_isSelecting = true;
				if (m_cursorType != TOOL_DELETE)
					m_cursorType = TOOL_SELECT;
				return true;
			}
		}
			
	}
	return false;
}

void CCanvas::deselectObjects()
{
	m_isSelecting = false;
	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++)
	{
		OBJECT *obj = *m_iterator;
		if (obj->isSelected == true)
			obj->isSelected = false;
	}
}

void CCanvas::moveSelectedObject()
{
	// Update movement
	m_isMoving = true;
	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++)
	{
		OBJECT *obj = *m_iterator;
		if (obj->isSelected == true)
		{
			if (obj->objectName == TOOL_RECTANGLE  || obj->objectName == TOOL_CIRCLE || obj->objectName == TOOL_LINE)
			{
				int h = obj->point[1].x - obj->point[0].x;
				int w = obj->point[1].y - obj->point[0].y;

				obj->point[0].x = obj->point[0].x + m_ptEnd.x - m_ptEnd0.x;
				obj->point[0].y = obj->point[0].y + m_ptEnd.y - m_ptEnd0.y;
				obj->point[1].x = obj->point[0].x + h;
				obj->point[1].y = obj->point[0].y + w;
			}
			// Avoid going though all contents of the list, unless all objects are selected
			if (!m_isSelectAll) 
				break;
		}
	}
}

void CCanvas::selectAll()
{
	if (!m_object.empty()){
		m_isSelecting = true;
		m_isSelectAll = true;

		for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++){
			OBJECT *obj = *m_iterator;
			obj->isSelected = true;
		}
	}
}

int CCanvas::update(CDC *dc)
{
	// Draw message at the upper part
	SetTextColor(*dc, RGB(0,0,127));
	dc->DrawText(m_message, CRect(0, 0, 800, 800),0);

		// Set pen
	CPen sensorPen(PS_SOLID, 10, OBSTACLE);
	CPen *oldPen = dc->SelectObject(&sensorPen);

	if (!m_isResizing){																		// 1st level rule
		if (isTooSmall()){																		// 2nd level rule
			//deselectObjects();
			if (isAnObjectSelected(m_ptEnd) && m_cursorType == TOOL_DELETE && !m_isMoving)
				deleteObject();
		}
		else{																					// 2nd level rule
			if (m_isSelecting)																	// 3rd level rule
				moveSelectedObject();
			else {																				// 3rd level rule
				if (m_cursorType == TOOL_RECTANGLE){
					CRect r(m_ptStart, m_ptEnd);
					dc->Rectangle(&r);
				}
				else if (m_cursorType == TOOL_CIRCLE){
					CRect r(m_ptStart, m_ptEnd);
					dc->Ellipse(&r);
				}
				else if (m_cursorType == TOOL_LINE){
					dc->MoveTo(m_ptStart);
					dc->LineTo(m_ptEnd);
				}
			}
		}
	}

	if (m_object.empty())
		return 0; // An empty storage

	int num = 0;
	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++){
		OBJECT *obj = *m_iterator;

		CRect r(obj->point[0], obj->point[1]);
		if (obj->objectName == TOOL_RECTANGLE)
			dc->Rectangle(&r);
		else if (obj->objectName == TOOL_CIRCLE)
			dc->Ellipse(&r);
		else if (obj->objectName == TOOL_LINE){
			dc->MoveTo(obj->point[0]);
			dc->LineTo(obj->point[1]);
		}
		num++;
		
		if (obj->objectName == TOOL_RECTANGLE || obj->objectName == TOOL_CIRCLE)
		{
			// Draw selection mark
			if (obj->isSelected)
			{
				m_centerL = CPoint(obj->point[0].x, obj->point[0].y + (obj->point[1].y - obj->point[0].y)/2);
				m_centerR = CPoint(obj->point[1].x, obj->point[0].y + (obj->point[1].y - obj->point[0].y)/2);
				m_centerT = CPoint(obj->point[0].x + (obj->point[1].x - obj->point[0].x)/2, obj->point[0].y);
				m_centerB = CPoint(obj->point[0].x + (obj->point[1].x - obj->point[0].x)/2, obj->point[1].y);
				m_topLCorner = CPoint(obj->point[0].x, obj->point[0].y); 
				m_topRCorner = CPoint(obj->point[1].x, obj->point[0].y); 
				m_bottomLCorner = CPoint(obj->point[0].x, obj->point[1].y);
				m_bottomRCorner = CPoint(obj->point[1].x, obj->point[1].y);

				drawHitMark(dc, 0);
			}
		}
		else if (obj->objectName == TOOL_LINE)
		{
			if (obj->isSelected)
			{
				m_topLCorner = CPoint(obj->point[0].x, obj->point[0].y);
				m_bottomRCorner = CPoint(obj->point[1].x, obj->point[1].y);

				drawHitMark(dc, 1);
			}
		}
	}
	// Return previous pen and brush
	dc->SelectObject(oldPen); 

	if (!m_isResizing)
		setResize();
	if (m_isResizing)
		resizeObject();

	return num; // Return number of objects in storage
}

int CCanvas::deleteObject()
{
	m_isSelecting = false;

	if (m_object.empty())
		return -1;
	
	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++)
	{
		OBJECT *obj = *m_iterator;
		if (obj->isSelected == true){
			m_object.remove(obj);
			break;
		}
	}

	return 0;
}

void CCanvas::setSnapToGrid(bool setting, int gridsizeX, int gridsizeY)
{
	m_isSnapToGrid = setting;
	m_gridsizeX = gridsizeX;
	m_gridsizeY = gridsizeY;
}

void CCanvas::set(CPoint ptStart, CPoint ptEnd)
{
	// Snap to grid
	if (m_isSnapToGrid)
		m_ptStart = CPoint(ptStart.x / m_gridsizeX * m_gridsizeX, ptStart.y / m_gridsizeX * m_gridsizeX);
	else
		m_ptStart = ptStart;

	// Save the old one first!
	m_ptEnd0 = m_ptEnd;

	// Snap to grid
	if (m_isSnapToGrid)
		m_ptEnd = CPoint(ptEnd.x / m_gridsizeY * m_gridsizeY, ptEnd.y / m_gridsizeY * m_gridsizeY);
	else
		m_ptEnd = ptEnd;

	// Never allow so much different between m_ptEnd and m_ptEnd0
	//   Dangerous!
	if (abs(m_ptEnd.x - m_ptEnd0.x) > 10 && abs(m_ptEnd.y - m_ptEnd0.y) > 10)
		m_ptEnd0 = m_ptEnd;
}

void CCanvas::set(int tool)
{
	m_cursorType = tool;
}

void CCanvas::initPoints()
{
	m_ptStart = CPoint(-1,-1);
	m_ptEnd = CPoint(-1,-1);
	m_ptEnd0 = CPoint(-1,-1);
}

void CCanvas::store()
{
	// If we want to store new object, it should be at least bigger then the minimum size
	//		and also not in selecting or resizing mode.
	if (!isTooSmall() && !m_isSelecting && !m_isResizing && m_cursorType != TOOL_SELECT){

		OBJECT* obj = (OBJECT*)malloc(sizeof(OBJECT));
		obj->objectName = m_cursorType;
		obj->point[0] = m_ptStart;
		obj->point[1] = m_ptEnd;
		obj->isSelected = false;
		m_object.push_back(obj);

		// After creating an object,the m_ptEnd will be at bottom right corner
		//   this will activate m_isResizing, so reset it!
		initPoints();
	}

	// Store is called when mouse key is up, so we can use this to re-init condition
	if (m_isResizing || m_isMoving || m_isSelectAll){
		m_isResizing = false;
		m_isMoving = false;
		m_isSelectAll = false;
		initPoints();
	}
}

bool CCanvas::loadFile(char *fileName)
{
	std::ifstream file(fileName);
	char str[20];
	
	if(file.is_open()){
		while(!file.eof()){
			OBJECT* obj = (OBJECT*)malloc(sizeof(OBJECT));
			file.getline(str, 20);
			obj->objectName = atoi(str);
			file.getline(str, 20);
			obj->point[0].x = atoi(str);
			file.getline(str, 20);
			obj->point[0].y = atoi(str);
			file.getline(str, 20);
			obj->point[1].x = atoi(str);
			file.getline(str, 20);
			obj->point[1].y = atoi(str);
			obj->isSelected = false;
			if (strcmp(str,""))	m_object.push_back(obj);
			else free(obj);
		}
		file.close();

		return true;
	}

	return false;
}

bool CCanvas::saveFile(char *fileName)
{
	std::ofstream file;
	file.open(fileName);

	if (m_object.empty())
		return false; // An empty storage

	for (m_iterator = m_object.begin(); m_iterator != m_object.end(); m_iterator++){
		OBJECT *obj = *m_iterator;
		file << obj->objectName << "\n";
		file << obj->point[0].x << "\n";
		file << obj->point[0].y << "\n";
		file << obj->point[1].x << "\n";
		file << obj->point[1].y << "\n";
	}

	file.close();

	return true;
}

void CCanvas::clearAll()
{
	m_isSelecting = false;
	m_object.clear();
}

void CCanvas::setMessage(char message[128])
{
	strcpy(m_message, message);
}

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, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Student
Indonesia Indonesia
http://kataauralius.com/

Comments and Discussions