#include <mil.h>
#include <math.h>
#include "FontUtil.h"
#include "CharacterCreator.h"
static const MIL_INT CASE_SIZE = 60;
const MIL_DOUBLE CIRCLE_RADIUS_RATIO = 0.8;
static const MIL_INT NAME_OFFSET_Y = 10;
static const MIL_INT HEX_OFFSET_Y = NAME_OFFSET_Y + 30;
static const MIL_INT CHAR_OFFSET_X = 30;
static const MIL_INT CHAR_OFFSET_Y = HEX_OFFSET_Y + 30;
CCharCreator::CCharCreator(MIL_ID MilSystem, MIL_INT FontSizeX, MIL_INT FontSizeY)
:m_MilDefaultDisplayBuffer(M_NULL),
m_MilDisplayBuffer(M_NULL),
m_MilCharacterBuffer(M_NULL),
m_FontSizeX(FontSizeX),
m_FontSizeY(FontSizeY)
{
MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, &m_MilDisplay);
MdispControl(m_MilDisplay, M_CENTER_DISPLAY, M_ENABLE);
MdispControl(m_MilDisplay, M_KEYBOARD_USE, M_DISABLE);
MdispControl(m_MilDisplay, M_MOUSE_USE, M_DISABLE);
MdispControl(m_MilDisplay, M_WINDOW_RESIZE, M_FULL_SIZE);
#if M_MIL_USE_WINDOWS
MdispControl(m_MilDisplay, M_WINDOW_SYSBUTTON, M_DISABLE);
#endif
MdispControl(m_MilDisplay, M_TITLE, MIL_TEXT("Dot Matrix Character"));
m_CaseSize = CASE_SIZE % 2 == 0 ? CASE_SIZE+ 1 : CASE_SIZE;
MIL_INT CharSizeX = FontSizeX * m_CaseSize + 1;
MIL_INT CharSizeY = FontSizeY * m_CaseSize + 1;
MIL_INT DisplaySizeX = CharSizeX + 2 * CHAR_OFFSET_X;
MIL_INT DisplaySizeY = CharSizeY + 2 * CHAR_OFFSET_Y;
MIL_INT Type = (8 + M_UNSIGNED);
MbufAlloc2d(MilSystem, DisplaySizeX, DisplaySizeY, Type,
M_IMAGE + M_PROC + M_DISP, &m_MilDefaultDisplayBuffer);
MbufChild2d(m_MilDefaultDisplayBuffer, CHAR_OFFSET_X, CHAR_OFFSET_Y,
CharSizeX, CharSizeY, &m_MilDefaultCharacterBuffer);
MbufAlloc2d(MilSystem, DisplaySizeX, DisplaySizeY, Type,
M_IMAGE + M_PROC + M_DISP, &m_MilDisplayBuffer);
MbufChild2d(m_MilDisplayBuffer, CHAR_OFFSET_X, CHAR_OFFSET_Y,
CharSizeX, CharSizeY, &m_MilCharacterBuffer);
MbufClear(m_MilDefaultDisplayBuffer, M_COLOR_WHITE);
MbufClear(m_MilDisplayBuffer, M_COLOR_WHITE);
MIL_INT NumberOfDots = FontSizeX * FontSizeY;
m_DotsPosX.resize(NumberOfDots);
m_DotsPosY.resize(NumberOfDots);
DrawGrid();
}
CCharCreator::~CCharCreator()
{
MbufFree(m_MilDefaultCharacterBuffer);
MbufFree(m_MilCharacterBuffer);
MbufFree(m_MilDefaultDisplayBuffer);
MbufFree(m_MilDisplayBuffer);
MdispFree(m_MilDisplay);
}
void CCharCreator::DrawGrid()
{
MgraColor(M_DEFAULT, M_COLOR_LIGHT_GRAY);
MIL_INT NumberOfLines = m_FontSizeX + 1 + m_FontSizeY+ 1;
vector<MIL_INT> LinesXStart(NumberOfLines);
vector<MIL_INT> LinesYStart(NumberOfLines);
vector<MIL_INT> LinesXEnd(NumberOfLines);
vector<MIL_INT> LinesYEnd(NumberOfLines);
MIL_INT Line = 0;
for (MIL_INT j = 0; j <= m_FontSizeY; j++)
{
LinesYStart[Line] = j * m_CaseSize;
LinesXStart[Line] = 0;
LinesYEnd[Line] = LinesYStart[Line];
LinesXEnd[Line] = m_FontSizeX * m_CaseSize;
Line++;
}
for (MIL_INT i = 0; i <= m_FontSizeX; i++)
{
LinesXStart[Line] = i * m_CaseSize;
LinesYStart[Line] = 0;
LinesXEnd[Line] = LinesXStart[Line];
LinesYEnd[Line] = m_FontSizeY * m_CaseSize;
Line++;
}
MIL_INT Dot = 0;
for (MIL_INT j = 0; j < m_FontSizeY; j++)
{
for (MIL_INT i = 0; i < m_FontSizeX; i++)
{
m_DotsPosX[Dot] = (MIL_INT)(1 + m_CaseSize * (i + 0.5));
m_DotsPosY[Dot] = (MIL_INT)(1 + m_CaseSize * (j + 0.5));
Dot++;
}
}
MgraLines(M_DEFAULT, m_MilDefaultCharacterBuffer, NumberOfLines,
&LinesXStart[0], &LinesYStart[0], &LinesXEnd[0], &LinesYEnd[0], M_LINE_LIST);
MgraDots(M_DEFAULT, m_MilDefaultCharacterBuffer, m_DotsPosX.size(),
&m_DotsPosX[0], &m_DotsPosY[0], M_DEFAULT);
}
bool CCharCreator::CreateCharacterInteractive(MIL_CONST_TEXT_PTR CharName,
MIL_CONST_TEXT_PTR CharHexName, bool Clear)
{
m_IsPressed = false;
m_IsSelected = false;
m_IsCleared = true;
m_ClearCaseIndex = 0;
if (Clear)
{
MbufCopy(m_MilDefaultDisplayBuffer, m_MilDisplayBuffer);
m_DotCharMatrix = vector<MIL_UINT8>(m_FontSizeX * m_FontSizeY, 0);
}
MdispSelect(m_MilDisplay, m_MilDisplayBuffer);
MgraControl(M_DEFAULT, M_BACKGROUND_MODE, M_TRANSPARENT);
MgraColor(M_DEFAULT, M_COLOR_BLACK);
MgraControl(M_DEFAULT, M_FONT_SIZE, 16);
MgraFont(M_DEFAULT, MIL_FONT_NAME(M_FONT_DEFAULT_TTF MIL_TEXT(":Bold")));
mstring CharNameString = MIL_TEXT("CharValue: ");
CharNameString += CharName;
DrawCharacterName(m_MilDisplayBuffer, CHAR_OFFSET_X, NAME_OFFSET_Y, CharNameString.c_str());
mstring CharHexNameString = MIL_TEXT("HexCharValue: ");
CharHexNameString += CharHexName;
MgraText(M_DEFAULT, m_MilDisplayBuffer, CHAR_OFFSET_X, HEX_OFFSET_Y, CharHexNameString.c_str());
MdispControl(m_MilDisplay, M_UPDATE, M_ENABLE);
MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE, HoverCaseHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_BUTTON_DOWN, MonitorPressButtonHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_DOUBLE_CLICK, MonitorPressButtonHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE, HoverModifyCaseHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_BUTTON_UP, MonitorReleaseButtonHook, this);
MosPrintf(MIL_TEXT("The Dot Matrix Character grid is displayed.\n")
MIL_TEXT("Use your mouse to interactively click on the cells\n")
MIL_TEXT("to define the dots that represent the character.\n"));
MosPrintf(MIL_TEXT("Press any key to complete the edition of the character.\n\n"));
MosGetch();
MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE + M_UNHOOK, HoverCaseHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_BUTTON_DOWN + M_UNHOOK, MonitorPressButtonHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_DOUBLE_CLICK + M_UNHOOK, MonitorPressButtonHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_MOVE + M_UNHOOK, HoverModifyCaseHook, this);
MdispHookFunction(m_MilDisplay, M_MOUSE_LEFT_BUTTON_UP + M_UNHOOK, MonitorReleaseButtonHook, this);
MdispSelect(m_MilDisplay, M_NULL);
bool HasOneDot = false;
for (MIL_UINT i = 0; i < m_DotCharMatrix.size() && !HasOneDot; i++)
HasOneDot = m_DotCharMatrix[i] == 0xFF;
return HasOneDot;
}
void CCharCreator::LoadCharacter(const vector<MIL_UINT8>& rDotCharMatrix)
{
m_DotCharMatrix = rDotCharMatrix;
DrawCharacterImage();
}
void CCharCreator::DrawCharacterImage()
{
MbufCopy(m_MilDefaultDisplayBuffer, m_MilDisplayBuffer);
for (MIL_UINT i = 0; i < m_DotCharMatrix.size(); i++)
{
if (!m_DotCharMatrix[i])
continue;
MIL_INT CenterCasePos = m_CaseSize / 2;
MIL_DOUBLE CircleRad = CIRCLE_RADIUS_RATIO * (MIL_DOUBLE)CenterCasePos;
MgraColor(M_DEFAULT, M_COLOR_BLACK);
MgraArcFill(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[i], m_DotsPosY[i],
CircleRad, CircleRad, 0.0, 360.0);
}
}
bool CCharCreator::DrawCharacterName(MIL_ID MilDest, MIL_INT OffsetX, MIL_INT OffsetY,
MIL_CONST_TEXT_PTR CharName)
{
MappControl(M_ERROR, M_PRINT_DISABLE);
MgraText(M_DEFAULT, MilDest, OffsetX, OffsetY, CharName);
MIL_INT DrawError = MappGetError(M_DEFAULT, M_CURRENT, M_NULL);
MappControl(M_ERROR, M_PRINT_ENABLE);
return DrawError == M_NULL_ERROR;
}
MIL_INT MFTYPE CCharCreator::HoverCaseHook(MIL_INT HookType, MIL_ID MilEvent, void *pUserData)
{
CCharCreator* pCharCreator = (CCharCreator*) pUserData;
return pCharCreator->HoverCase(MilEvent);
}
MIL_INT CCharCreator::HoverCase(MIL_ID MilEvent)
{
if (m_IsPressed)
return 0;
MIL_DOUBLE MousePosX;
MIL_DOUBLE MousePosY;
MdispGetHookInfo(MilEvent, M_MOUSE_POSITION_BUFFER_X, &MousePosX);
MdispGetHookInfo(MilEvent, M_MOUSE_POSITION_BUFFER_Y, &MousePosY);
MIL_INT CaseIndex, PosInCaseX, PosInCaseY;
bool IsValid = CalculatePositions(MousePosX, MousePosY, &CaseIndex,
&PosInCaseX, &PosInCaseY);
if (!IsValid || m_DotCharMatrix[CaseIndex])
{
ClearCase();
return 0;
}
MIL_INT CenterCasePos = m_CaseSize / 2;
MIL_DOUBLE DiffX = fabs((MIL_DOUBLE)(CenterCasePos - PosInCaseX));
MIL_DOUBLE DiffY = fabs((MIL_DOUBLE)(CenterCasePos - PosInCaseY));
MIL_DOUBLE Distance = sqrt(DiffX * DiffX + DiffY * DiffY);
MIL_DOUBLE CircleRad = CIRCLE_RADIUS_RATIO * (MIL_DOUBLE)CenterCasePos;
if (Distance > CircleRad)
{
ClearCase();
return 0;
}
if (m_ClearCaseIndex != CaseIndex)
ClearCase();
if (!m_IsCleared)
return 0;
m_ClearCaseIndex = CaseIndex;
m_IsCleared = false;
MgraColor(M_DEFAULT, M_COLOR_LIGHT_GRAY);
MgraArc(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[CaseIndex], m_DotsPosY[CaseIndex],
CircleRad, CircleRad, 0.0, 360.0);
return 0;
}
MIL_INT MFTYPE CCharCreator::HoverModifyCaseHook(MIL_INT HookType, MIL_ID MilEvent,
void *pUserData)
{
CCharCreator* pCharCreator = (CCharCreator*) pUserData;
return pCharCreator->HoverModifyCase(MilEvent);
}
MIL_INT CCharCreator::HoverModifyCase(MIL_ID MilEvent)
{
if (!m_IsPressed)
return 0;
ModifyCase(MilEvent);
return 0;
}
MIL_INT MFTYPE CCharCreator::MonitorPressButtonHook(MIL_INT HookType, MIL_ID MilEvent,
void *pUserData)
{
CCharCreator* pCharCreator = (CCharCreator*) pUserData;
return pCharCreator->MonitorPressButton(MilEvent);
}
MIL_INT CCharCreator::MonitorPressButton(MIL_ID MilEvent)
{
m_IsPressed = true;
ModifyCase(MilEvent);
m_IsCleared = true;
return 0;
}
MIL_INT MFTYPE CCharCreator::MonitorReleaseButtonHook(MIL_INT HookType, MIL_ID MilEvent,
void *pUserData)
{
CCharCreator* pCharCreator = (CCharCreator*) pUserData;
return pCharCreator->MonitorReleaseButton(MilEvent);
}
MIL_INT CCharCreator::MonitorReleaseButton(MIL_ID MilEvent)
{
m_IsPressed = false;
m_IsSelected = false;
return 0;
}
bool CCharCreator::CalculatePositions(MIL_DOUBLE PosX, MIL_DOUBLE PosY, MIL_INT* pCaseIndex, MIL_INT* pPosInCaseX, MIL_INT* pPosInCaseY)
{
MIL_INT CaseX = (MIL_INT)(floor((PosX - (MIL_DOUBLE)CHAR_OFFSET_X - 1.0) /
(MIL_DOUBLE)(m_CaseSize + 1)));
MIL_INT CaseY = (MIL_INT)(floor((PosY - (MIL_DOUBLE)CHAR_OFFSET_Y - 1.0) /
(MIL_DOUBLE)(m_CaseSize + 1)));
if ((CaseX >= 0 && CaseX < m_FontSizeX) && (CaseY >= 0 && CaseY < m_FontSizeY))
{
*pPosInCaseX = ((MIL_INT)PosX - CHAR_OFFSET_X) - CaseX * m_CaseSize;
*pPosInCaseY = ((MIL_INT)PosY - CHAR_OFFSET_Y) - CaseY * m_CaseSize;
}
else
return false;
*pCaseIndex = CaseX + CaseY * m_FontSizeX;
return true;
}
void CCharCreator::ModifyCase(MIL_ID MilEvent)
{
MIL_DOUBLE MousePosX;
MIL_DOUBLE MousePosY;
MdispGetHookInfo(MilEvent, M_MOUSE_POSITION_BUFFER_X, &MousePosX);
MdispGetHookInfo(MilEvent, M_MOUSE_POSITION_BUFFER_Y, &MousePosY);
MIL_INT CaseIndex, PosInCaseX, PosInCaseY;
bool IsValid = CalculatePositions(MousePosX, MousePosY, &CaseIndex, &PosInCaseX, &PosInCaseY);
if (!IsValid)
return;
MIL_INT CenterCasePos = m_CaseSize / 2;
MIL_DOUBLE DiffX = fabs((MIL_DOUBLE)(CenterCasePos - PosInCaseX));
MIL_DOUBLE DiffY = fabs((MIL_DOUBLE)(CenterCasePos - PosInCaseY));
MIL_DOUBLE Distance = sqrt(DiffX * DiffX + DiffY * DiffY);
MIL_DOUBLE CircleRad = CIRCLE_RADIUS_RATIO * (MIL_DOUBLE)CenterCasePos;
if (Distance > CircleRad)
{
m_IsSelected = false;
return;
}
if (m_IsSelected)
return;
if (m_DotCharMatrix[CaseIndex])
{
MgraColor(M_DEFAULT, M_COLOR_WHITE);
MdispControl(m_MilDisplay, M_UPDATE, M_DISABLE);
MgraArcFill(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[CaseIndex], m_DotsPosY[CaseIndex],
CircleRad, CircleRad, 0.0, 360.0);
MgraColor(M_DEFAULT, M_COLOR_LIGHT_GRAY);
MgraDot(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[CaseIndex], m_DotsPosY[CaseIndex]);
MdispControl(m_MilDisplay, M_UPDATE, M_ENABLE);
m_DotCharMatrix[CaseIndex] = 0;
}
else
{
MgraColor(M_DEFAULT, M_COLOR_BLACK);
MgraArcFill(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[CaseIndex], m_DotsPosY[CaseIndex],
CircleRad, CircleRad, 0.0, 360.0);
m_DotCharMatrix[CaseIndex] = 0xFF;
}
m_IsSelected = true;
}
void CCharCreator::ClearCase()
{
if (m_IsCleared || m_DotCharMatrix[m_ClearCaseIndex])
return;
MIL_INT CenterCasePos = m_CaseSize / 2;
MIL_DOUBLE CircleRad = CIRCLE_RADIUS_RATIO * (MIL_DOUBLE)CenterCasePos;
MgraColor(M_DEFAULT, M_COLOR_WHITE);
MgraArc(M_DEFAULT, m_MilCharacterBuffer, m_DotsPosX[m_ClearCaseIndex], m_DotsPosY[m_ClearCaseIndex],
CircleRad, CircleRad, 0.0, 360.0);
m_IsCleared = true;
}