// 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);
}