// Digistring.cpp : implementation file
//
// Copyright (C) 2000 by Michel Wassink
// All rights reserved
//
// This is free software.
// 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 and all copyright   
// notices remains intact. If the source code in this file is used in   
// any  commercial application then a statement along the lines of   
// "Portions Copyright © 2000 Michel Wassink" must be included in   
// the startup banner, "About" box or printed documentation. An email   
// letting me know that you are using it would be nice as well. That's   
// not much to ask considering the amount of work that went into this.  
//  
// No warrantee of any kind, expressed or implied, is included with this
// software; use at your own risk, responsibility for damages (if any) to
// anyone resulting from the use of this software rests entirely with the
// user.
//
// Send bug reports, bug fixes, enhancements, requests, flames, etc., and
// I'll try to keep a version up to date.  I can be reached as follows:
//    mwassink@csi.com				  (private site)
// An email letting me know that you are using it would be nice.
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Digistring.h"
#include "Curvefit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Segment numbering:
// -----          13		 -----          0 
//|\ | /|      8  0  12		|     |      1     2  
//| \|/ |        1 2		|     |            
// -- --   ==    6 7	     -----   ==     3     
//| /|\ |        3 4	    |     |               
//|/ | \|      9  5  11		|     |      4     5  
// -----          10	     -----          6    

#define MAXSEGCHAR7		12		// Number of supported 7 segment characters
#define MAXSEGCHAR14	40		// Number of supported 14 segment characters
#define MAXSEGSEMCOL	 2		// Number of supported 3 segment characters
#define NORM_DIGIHEIGHT	83		// All characters must have this height

////////////////////////////////////////////////////////////////////////////////
// For 14 segments display...
//									   SP		0		1		2		3		4		5		6	
WORD CHAR_SEGM14[MAXSEGCHAR14]  = {0x0000, 0x3F00, 0x1800, 0x36C0, 0x3CC0, 0x19C0, 0x2DC0, 0x2FC0, 
//		7		8		9		-		A		B		C		D		E		F		G		H
   0x3800, 0x3FC0, 0x3DC0, 0x00C0, 0x3BC0, 0x3CA1, 0x2700, 0x3C21, 0x27C0, 0x23C0, 0x2F80, 0x1BC0,
//		I		J		K		L		M		N		O		P		Q		R		S
   0x2421, 0x1E00, 0x0354, 0x0700, 0x1B06, 0x1B12, 0x3F00, 0x33C0, 0x3F10, 0x33D0, 0x2DC0,
//		T		U		V		W		X		Y		Z		*		+
   0x2021, 0x1F00, 0x030C, 0x1B18, 0x001E, 0x11E0, 0x240C, 0x00FF, 0x00E1};
// straight style
CPoint PtSeg14N0[5]	 = {CPoint(20,13), CPoint(20,36), CPoint(24,40), CPoint(28,36), CPoint(28,13)};
CPoint PtSeg14N1[4]	 = {CPoint( 5, 5), CPoint(15,35), CPoint(20,37), CPoint(18,25)};
CPoint PtSeg14N6[6]	 = {CPoint( 6,41), CPoint(14,45), CPoint(18,45), CPoint(22,41), CPoint(18,37), 
						CPoint(14,37)};
CPoint PtSeg14N8[4]	 = {CPoint( 4, 7), CPoint( 4,40), CPoint(11,36), CPoint(11,26)};
CPoint PtSeg14N13[4] = {CPoint( 6, 4), CPoint(11,11), CPoint(37,11), CPoint(42, 4)};
BYTE   TpSeg14N0[5]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO,		PT_LINETO};
BYTE   TpSeg14N1[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSeg14N6[6]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO,		PT_LINETO, 
						PT_LINETO};
BYTE   TpSeg14N8[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSeg14N13[4] = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};

// smooth style PT_BEZIERTO
CPoint PtSeg14N0S[13] ={CPoint(20,12), CPoint(20,25), CPoint(22,36), CPoint(23,39), CPoint(24,40), 
						CPoint(25,39), CPoint(26,36), CPoint(28,25), CPoint(28,12), CPoint(26,10),
						CPoint(24, 9), CPoint(22,10), CPoint(20,12)};
CPoint PtSeg14N1S[10] ={CPoint(10,10), CPoint(10,13), CPoint(11,20), CPoint(13,28), CPoint(21,38), 
						CPoint(21,37), CPoint(19,26), CPoint(15,16), CPoint(11,10), CPoint(10,10)};
CPoint PtSeg14N6S[6] = {CPoint( 8,41), CPoint(12,45), CPoint(16,45), CPoint(23,41), CPoint(16,37),
						CPoint(12,37)};
CPoint PtSeg14N8S[10]= {CPoint( 4, 7), CPoint( 4,39), CPoint( 5,40), CPoint( 6,40), CPoint( 9,37),
						CPoint(11,33), CPoint(11,25), CPoint( 9,14), CPoint( 5, 6), CPoint( 4, 7)};
CPoint PtSeg14N13S[17]={CPoint( 8, 4), CPoint( 7, 5), CPoint( 7, 6), CPoint( 9, 8), CPoint(12, 9),
						CPoint(14,11), CPoint(19,11), CPoint(21, 9), CPoint(24, 7), CPoint(27, 9),
						CPoint(29,11), CPoint(34,11), CPoint(36, 9), CPoint(39, 8), CPoint(41, 6),
						CPoint(41, 5), CPoint(40, 4)};
BYTE   TpSeg14N0S[13]= {PT_MOVETO,	   PT_LINETO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO,
						PT_BEZIERTO,   PT_BEZIERTO,	  PT_LINETO,	 PT_LINETO,		PT_BEZIERTO,
						PT_BEZIERTO,   PT_BEZIERTO,	  PT_LINETO};
BYTE   TpSeg14N1S[10] ={PT_MOVETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_LINETO,
						PT_LINETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_LINETO};
BYTE   TpSeg14N6S[6] = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO,		PT_LINETO, 
						PT_LINETO};
BYTE   TpSeg14N8S[10] ={PT_MOVETO,	   PT_LINETO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO,
						PT_BEZIERTO,   PT_LINETO,	  PT_BEZIERTO,   PT_BEZIERTO,   PT_LINETO};
BYTE   TpSeg14N13S[17]={PT_MOVETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO, 
						PT_LINETO,	   PT_LINETO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO,
						PT_LINETO,	   PT_LINETO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO,
						PT_BEZIERTO,   PT_LINETO};
///////////////////////////////////////////////////////////////////////////////
// For 7 segments display...
//									 SP		0	  1		2	  3		4	  5
BYTE CHAR_SEGM7[MAXSEGCHAR7]    = {0x00, 0x77, 0x24, 0x5D, 0x6D, 0x2E, 0x6B, 
// 	  6		7	  8		9	  -
   0x7B, 0x25, 0x7F, 0x6F, 0x08};
// straight style
CPoint PtSeg7N0[4]	 = {CPoint( 5, 4), CPoint(12,11), CPoint(36,11), CPoint(43, 4)};
CPoint PtSeg7N1[4]	 = {CPoint( 4, 6), CPoint( 4,40), CPoint(11,36), CPoint(11,13)};
CPoint PtSeg7N3[6]	 = {CPoint( 6,41), CPoint(14,45), CPoint(34,45), CPoint(42,41), CPoint(34,37),
						CPoint(14,37)}; // 3
BYTE   TpSeg7N0[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSeg7N1[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSeg7N3[6]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO,		PT_LINETO,
						PT_LINETO};

// smooth style PT_BEZIERTO
CPoint PtSeg7N0S[7]	 = {CPoint( 6, 4), CPoint( 5, 5), CPoint( 5, 6), CPoint( 8, 9), CPoint(12,11),
						CPoint(36,11), CPoint(39, 4)};
CPoint PtSeg7N1S[7]	 = {CPoint( 4, 9), CPoint( 4,39), CPoint( 6,40), CPoint( 7,40), CPoint( 9,38),
						CPoint(11,36), CPoint(11,12)};
CPoint PtSeg7N2S[10] = {CPoint(37,36), CPoint(39,38), CPoint(41,40), CPoint(42,40), CPoint(44,39),
						CPoint(44, 6), CPoint(42, 4), CPoint(41, 4), CPoint(39, 8), CPoint(37,12)};
CPoint PtSeg7N3S[6]	 = {CPoint( 8,41), CPoint(12,45), CPoint(36,45), CPoint(40,41), CPoint(36,37),
						CPoint(12,37)};
BYTE   TpSeg7N0S[7]	 = {PT_MOVETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_LINETO,
						PT_LINETO,	   PT_LINETO};
BYTE   TpSeg7N1S[7]	 = {PT_MOVETO,	   PT_LINETO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_BEZIERTO,
						PT_LINETO,	   PT_LINETO};
BYTE   TpSeg7N2S[10] = {PT_MOVETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_LINETO,		PT_LINETO,
						PT_LINETO,	   PT_BEZIERTO,	  PT_BEZIERTO,	 PT_BEZIERTO,	PT_LINETO};
BYTE   TpSeg7N3S[6]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO,		PT_LINETO,
						PT_LINETO};

// For 3 segments semicoloncombi display...
//									  .		:
BYTE CHAR_SEMCOL[MAXSEGSEMCOL]  = {0x04, 0x03};

CPoint PtSegScN0[4]	 = {CPoint( 4,23), CPoint( 4,32), CPoint(13,32), CPoint(13,23)};
CPoint PtSegScN1[4]	 = {CPoint( 4,50), CPoint( 4,59), CPoint(13,59), CPoint(13,50)};
CPoint PtSegScN2[4]	 = {CPoint( 4,68), CPoint( 4,77), CPoint(13,77), CPoint(13,68)};
BYTE   TpSegScN0[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSegScN1[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};
BYTE   TpSegScN2[4]	 = {PT_MOVETO,	   PT_LINETO,	  PT_LINETO,	 PT_LINETO};

// Functions for mirroring points...
CPoint PointMirror(const CPoint &P, const CPoint &M)
{
	return CPoint(P.x + 2*(M.x - P.x), P.y + 2*(M.y - P.y));
}

CPoint LineMirrorX(const CPoint &P, int X)
{
	return CPoint(P.x + 2*(X - P.x), P.y);
}

CPoint LineMirrorY(const CPoint &P, int Y)
{
	return CPoint(P.x, P.y + 2*(Y - P.y));
}

/////////////////////////////////////////////////////////////////////////////
// CDSegment
CDSegment::CDSegment()
{
	m_paPoints = NULL;
	m_paTypes = NULL;
}

CDSegment::CDSegment(const CDSegment& Segment)
{
	ASSERT(Segment.m_paPoints != NULL); // Do not copy an unitialized segment

	m_nCount = Segment.m_nCount;

	m_paPoints = new CPoint[m_nCount];
	m_paTypes = new BYTE[m_nCount];
	if (m_paPoints != NULL && m_paTypes != NULL)
	{
		memcpy(m_paPoints, Segment.m_paPoints, m_nCount * sizeof(CPoint));
		memcpy(m_paTypes, Segment.m_paTypes, m_nCount * sizeof(BYTE));
	}
}

// -----------------------------------------------------------------------------

CDSegment::~CDSegment()
{
	FreeSegment();
}

void CDSegment::DefPoints(const POINT* lpaPoints, const BYTE* lpaTypes, int nCount)
{
	FreeSegment();

	m_paPoints = new CPoint[nCount];
	m_paTypes = new BYTE[nCount];
	m_nCount = nCount;
	if (m_paPoints != NULL && m_paTypes != NULL)
	{
		memcpy(m_paPoints, lpaPoints, m_nCount * sizeof(CPoint));
		memcpy(m_paTypes, lpaTypes, m_nCount * sizeof(BYTE));
	}
}

// -----------------------------------------------------------------------------

CDSegment CDSegment::operator=(const CDSegment &Segment)
{
	CPoint *pNewPoints;
	BYTE * pNewTypes;

	m_nCount = Segment.m_nCount;

	pNewPoints = new CPoint[m_nCount];
	pNewTypes = new BYTE[m_nCount];
	memcpy(pNewPoints, Segment.m_paPoints, m_nCount * sizeof(CPoint));
	memcpy(pNewTypes, Segment.m_paTypes, m_nCount * sizeof(BYTE));
	FreeSegment();			// Get rid of old stuff
	m_paPoints = pNewPoints;
	m_paTypes = pNewTypes;

	return *this;
}

void CDSegment::FreeSegment()
{
	if (m_paPoints != NULL && m_paTypes != NULL)
	{
		delete[] m_paPoints;
		delete[] m_paTypes;
		m_paPoints = NULL;
		m_paTypes = NULL;	
	}
}

void CDSegment::Draw(CDC *pDC, CDoubleRect DrawPlace, int iWidth) const
{
	int i, nBez,b;
	CPoint * paPoints;
	double daContr[4];
	double *pBezPts;
	double dRelWidth, dRelHeight;

	paPoints = new CPoint[m_nCount];
	if (paPoints == NULL) return;

	dRelWidth = DrawPlace.Width() / iWidth;
	dRelHeight = DrawPlace.Height() / NORM_DIGIHEIGHT;
	for (i = 0; i < m_nCount; i++)
	{
		if (m_paTypes[i] != PT_BEZIERTO)
		{
			paPoints[i] = CPoint(DrawPlace.left + dRelWidth	 * m_paPoints[i].x + 0.5,
								 DrawPlace.top  + dRelHeight * m_paPoints[i].y + 0.5);
		}
	}

	for (i = 0; i < m_nCount; i++)
	{
		if (m_paTypes[i] == PT_MOVETO)
		{
			pDC->MoveTo(paPoints[i]);
		}
		else if (m_paTypes[i] == PT_LINETO)
		{
			VERIFY(pDC->LineTo(paPoints[i]));
		}
		else if (m_paTypes[i] == PT_BEZIERTO)
		{
			// Look for the first non-bezier point(This is the EndPoint)...
			nBez = 0;
			do
			{
				nBez++;
			} while (m_paTypes[i+nBez] == PT_BEZIERTO);

			pBezPts = new double[2*(nBez+2)];
			for (b = 0; b < (nBez+2)*2; b += 2)
			{
				pBezPts[b  ] = DrawPlace.left + dRelWidth	* m_paPoints[i-1+b/2].x; 
				pBezPts[b+1] = DrawPlace.top  + dRelHeight	* m_paPoints[i-1+b/2].y;
			}
			CalcBezier(pBezPts, 2*(nBez+2), daContr);
			delete[] pBezPts;

			paPoints[i  ].x	= daContr[0] + 0.5;
			paPoints[i  ].y	= daContr[1] + 0.5;
			paPoints[i+1].x	= daContr[2] + 0.5;
			paPoints[i+1].y	= daContr[3] + 0.5;
			paPoints[i+2]	= paPoints[i+nBez];

			VERIFY(pDC->PolyBezierTo(&paPoints[i], 3));
			i += nBez;
		}
	} // for
	delete[] paPoints;
}

/////////////////////////////////////////////////////////////////////////////
// CDigiChar

CDigiChar::CDigiChar()
{
	m_Width = 49;
	m_wSegmData = 0x0000; // All segments in offcolor
}

CDigiChar::~CDigiChar()
{
	
}

void CDigiChar::Draw(CDC *pDC, CDoubleRect DrawPlace, CPen *pOffPen, CPen *pOnPen,
					 CBrush *pOffBrush, CBrush *pOnBrush) 
{
	WORD SegMask;
	DSegmentVector::iterator SegmIterator;

	SegMask = 1;
	for (SegmIterator = m_SegmentVector.begin(); SegmIterator != m_SegmentVector.end();
         SegmIterator++)
	{
		if (m_wSegmData & SegMask)
		{
			pDC->SelectObject(pOnBrush);
			pDC->SelectObject(pOnPen);
		}
		else
		{
			pDC->SelectObject(pOffBrush);
			pDC->SelectObject(pOffPen);
		}

		pDC->BeginPath();
		SegmIterator->Draw(pDC, DrawPlace, m_Width);
		pDC->EndPath();
		pDC->StrokeAndFillPath();

		SegMask <<= 1;
	}
}
void CDigiChar::SetElementData(WORD wSegmData, int /*iDispStyle*/)
{
	m_wSegmData = wSegmData;
}

int CDigiChar::GetNormWidth() const
{
	return m_Width;
}

/////////////////////////////////////////////////////////////////////////////
// CDigi7Segment

CDigi7Segment::CDigi7Segment()
{
	m_Width = 49;
	m_NSegments = 7;
}

void CDigi7Segment::SetElementData(WORD wSegmData, int iDispStyle)
{
	int i, p;
	CDSegment TmpSegm;
	LPPOINT lpTmpSegPoints = NULL;
	LPPOINT lpSegPoints;
	LPBYTE lpType;
	int nCount;

	m_SegmentVector.clear();
	for (i = 0; i < m_NSegments; i++)
	{
		switch(i)
		{
			case 0:
			case 6: if (iDispStyle & CDigistring::DS_SMOOTH)
					{
						lpSegPoints = PtSeg7N0S;
						lpType = TpSeg7N0S; 
						nCount = 7; 
					}
					else
					{
						lpSegPoints = PtSeg7N0;
						lpType = TpSeg7N0; 
						nCount = 4; 
					}
					break;
			case 1:
			case 4:if (iDispStyle & CDigistring::DS_SMOOTH)
					{
						lpSegPoints = PtSeg7N1S;
						lpType = TpSeg7N1S; 
						nCount = 7; 
					}
					else
					{
						lpSegPoints = PtSeg7N1;
						lpType = TpSeg7N1; 
						nCount = 4; 
					}
					break;
			case 2:
			case 5: if (iDispStyle & CDigistring::DS_SMOOTH)
					{
						lpSegPoints = PtSeg7N2S;
						lpType = TpSeg7N2S; 
						nCount = 10; 
					}
					else
					{
						lpSegPoints = PtSeg7N1;
						lpType = TpSeg7N1; 
						nCount = 4; 
					}
					break;
			case 3: if (iDispStyle & CDigistring::DS_SMOOTH)
					{
						lpSegPoints = PtSeg7N3S;
						lpType = TpSeg7N3S; 
						nCount = 6; 
					}
					else
					{
						lpSegPoints = PtSeg7N3;
						lpType = TpSeg7N3; 
						nCount = 6; 
					}
					break;
		}

		switch(i)
		{
		case 6: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		case 2: if (!(iDispStyle & CDigistring::DS_SMOOTH))
				{
					lpTmpSegPoints = new CPoint[nCount]; 
					for (p = 0; p < nCount; p++)
						lpTmpSegPoints[p] = LineMirrorX(lpSegPoints[p], (m_Width-1)/2);
				}
				break;
		case 4: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		case 5: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				if (iDispStyle & CDigistring::DS_SMOOTH)
					lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);
				else
					lpTmpSegPoints[p] = PointMirror(lpSegPoints[p], CPoint((m_Width-1)/2, 41));
				break;
		}

		if (lpTmpSegPoints == NULL)
			TmpSegm.DefPoints(lpSegPoints, lpType, nCount);
		else
		{
			TmpSegm.DefPoints(lpTmpSegPoints, lpType, nCount);
			delete[] lpTmpSegPoints;
			lpTmpSegPoints = NULL;
		}
		m_SegmentVector.push_back(TmpSegm);
	}
	m_wSegmData = wSegmData;
}

/////////////////////////////////////////////////////////////////////////////
// CDigi14Segment

CDigi14Segment::CDigi14Segment()
{
	m_Width = 49;
	m_NSegments = 14;
}

void CDigi14Segment::SetElementData(WORD wSegmData, int iDispStyle)
{
	int i, p;
	CDSegment x;
	LPPOINT lpTmpSegPoints = NULL;
	LPPOINT lpSegPoints;
	LPBYTE lpType;
	int nCount;

	m_SegmentVector.clear();
	for (i = 0; i < m_NSegments; i++)
	{
		switch(i)
		{
			case 0:
			case 5:	if (iDispStyle & CDigistring::DS_SMOOTH)
					{
						lpSegPoints = PtSeg14N0S;
						lpType = TpSeg14N0S; 
						nCount = 13;
					}
					else
					{
						lpSegPoints = PtSeg14N0;
						lpType = TpSeg14N0; 
						nCount = 5;
					}
				break;
			case 1:
			case 2:
			case 3:
			case 4:
				if (iDispStyle & CDigistring::DS_SMOOTH)
				{
					lpSegPoints = PtSeg14N1S;
					lpType = TpSeg14N1S; 
					nCount = 10;
				}
				else
				{
					lpSegPoints = PtSeg14N1;
					lpType = TpSeg14N1; 
					nCount = 5;
				}
				break;
			case 6:
			case 7:
				if (iDispStyle & CDigistring::DS_SMOOTH)
				{
					lpSegPoints = PtSeg14N6S;
					lpType = TpSeg14N6S; 
					nCount = 6;
				}
				else
				{
					lpSegPoints = PtSeg14N6;
					lpType = TpSeg14N6; 
					nCount = 6;
				}
				break;
			case 8:
			case 9:
			case 11:
			case 12:
				if (iDispStyle & CDigistring::DS_SMOOTH)
				{
					lpSegPoints = PtSeg14N8S;
					lpType = TpSeg14N8S; 
					nCount = 10;
				}
				else
				{
					lpSegPoints = PtSeg14N8;
					lpType = TpSeg14N8; 
					nCount = 4;
				}
				break;
			case 13:
			case 10:
				if (iDispStyle & CDigistring::DS_SMOOTH)
				{
					lpSegPoints = PtSeg14N13S;
					lpType = TpSeg14N13S; 
					nCount = 17;
				}
				else
				{
					lpSegPoints = PtSeg14N13;
					lpType = TpSeg14N13; 
					nCount = 4;
				}
				break;
		}

		switch(i)
		{
		case 5: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		case 2: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorX(lpSegPoints[p], (m_Width-1)/2);break;
		case 3: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		case 4: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = PointMirror(lpSegPoints[p], CPoint((m_Width-1)/2, 41));break;
		case 7: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorX(lpSegPoints[p], (m_Width-1)/2);break;
		case 9: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		case 11: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = PointMirror(lpSegPoints[p], CPoint((m_Width-1)/2, 41));break;
		case 12: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorX(lpSegPoints[p], (m_Width-1)/2);break;
		case 10: lpTmpSegPoints = new CPoint[nCount]; 
			for (p = 0; p < nCount; p++)
				lpTmpSegPoints[p] = LineMirrorY(lpSegPoints[p], 41);break;
		}

		if (lpTmpSegPoints == NULL)
			x.DefPoints(lpSegPoints, lpType, nCount);
		else
		{
			x.DefPoints(lpTmpSegPoints, lpType, nCount);
			delete[] lpTmpSegPoints;
			lpTmpSegPoints = NULL;
		}
		m_SegmentVector.push_back(x);
	}
	m_wSegmData = wSegmData;
}

/////////////////////////////////////////////////////////////////////////////
// CDigiSemiColonCombi

CDigiColonDotChar::CDigiColonDotChar()
{
	m_Width = 18;
	m_NSegments = 3;
}

void CDigiColonDotChar::SetElementData(WORD wSegmData, int /*iDispStyle*/)
{
	int i;
	CDSegment DSegment;
	LPPOINT lpSegPoints;
	LPBYTE lpType;
	int nCount; 

	m_SegmentVector.clear();

	for (i = 0; i < m_NSegments; i++)
	{
		switch(i)
		{
			case 0:lpSegPoints = PtSegScN0;
				lpType = TpSegScN0; 
				nCount = 4; break;
			case 1:
				lpSegPoints = PtSegScN1;
				lpType = TpSegScN1; 
				nCount = 4; break;
			case 2:
				lpSegPoints = PtSegScN2;
				lpType = TpSegScN2; 
				nCount = 4; break;
		}
		DSegment.DefPoints(lpSegPoints, lpType, nCount);
		m_SegmentVector.push_back(DSegment);
	}

	m_wSegmData = wSegmData;
}

/////////////////////////////////////////////////////////////////////////////
// CDigistring

CDigistring::CDigistring()
	: m_strText("EMPTY")
{
	m_DispStyle = DS_SZ_PROP;
	m_BackColor = BLACK;
	m_Modified = TRUE;
	m_OffColor = DARKGREEN;
	m_OnColor = LIGHTGREEN;
}

CDigistring::~CDigistring()
{
	m_CharVector.clear();
}

void CDigistring::SetText(LPCTSTR lpszText)
{
	if (m_strText != lpszText)
	{
		m_strText = lpszText;
		m_Modified = TRUE;
		Invalidate();
	}
}

void CDigistring::Format(LPCTSTR lpszFormat, ...)
{
	TCHAR szMessage[256];

	va_list argp;
	va_start(argp, lpszFormat);
	_vsntprintf(szMessage, 255, lpszFormat, argp);
	va_end(argp);                               

	SetText(szMessage);
}

void CDigistring::SetColor(COLORREF OffColor, COLORREF OnColor)
{
	if (m_OnColor == OnColor && m_OffColor == OffColor)
		return;
	m_OnColor = OnColor;
	m_OffColor = OffColor;
	Invalidate();
}

void CDigistring::SetBackColor(COLORREF BackColor /* = BLACK */)
{
	if (m_BackColor == BackColor)
		return;
	m_BackColor = BackColor;
	Invalidate();
}

BOOL CDigistring::ModifyDigiStyle(DWORD dwRemove, DWORD dwAdd)
{
	ASSERT(!(dwRemove & dwAdd));
	if (dwRemove & dwAdd)
		return FALSE;

	m_DispStyle |= dwAdd;
	m_DispStyle &= ~dwRemove;

	m_Modified = TRUE;
	Invalidate();
	return TRUE;
}

CDigiChar * CDigistring::DefineChar(TCHAR cChar)
{
	int iIndex;
	CDigiChar * pDChar = NULL;

	if (cChar >= _T('0') && cChar <= _T('9') 
		|| cChar == _T(' ') || cChar == _T('-'))
	{
		if (cChar == _T(' '))
		{
			iIndex = 0;
		}
		else if (cChar == _T('-'))
		{
			iIndex = 11;
		}
		else
		{
			iIndex = cChar - _T('0') + 1;
		}

		if (m_DispStyle & DS_STYLE14)
		{
			pDChar = new CDigi14Segment;
			pDChar->SetElementData(CHAR_SEGM14[iIndex], m_DispStyle);
		}
		else
		{
			pDChar = new CDigi7Segment;
			pDChar->SetElementData(CHAR_SEGM7[iIndex], m_DispStyle);
		}
	}
	else if (cChar >= _T('A') && cChar <= _T('Z'))
	{
		iIndex = cChar - _T('A') + 12;
			pDChar = new CDigi14Segment;
			pDChar->SetElementData(CHAR_SEGM14[iIndex], m_DispStyle);
	}
	else
	{
		iIndex = 0;
		switch(cChar)
		{
			case _T(':'): iIndex++; 
			case _T('.'): pDChar = new CDigiColonDotChar; 
					pDChar->SetElementData(CHAR_SEMCOL[iIndex], m_DispStyle);
					break;
			case _T('*'): iIndex = MAXSEGCHAR14 - 2; pDChar = new CDigi14Segment; 
					pDChar->SetElementData(CHAR_SEGM14[iIndex], m_DispStyle);
				break;
			case _T('+'): iIndex = MAXSEGCHAR14 - 1; pDChar = new CDigi14Segment; 
					pDChar->SetElementData(CHAR_SEGM14[iIndex], m_DispStyle);
				break;
			default : ASSERT(FALSE);
		}
	}
	return pDChar;
}

void CDigistring::BuildString()
{
	CDigiChar * pDChar;
	if (!m_Modified) return;
	m_CharVector.clear();
	
	m_strText.MakeUpper();
	for (int i = 0; i < m_strText.GetLength(); i++)
	{
		if ((pDChar = DefineChar(m_strText[i])) != NULL)
		{
			m_CharVector.push_back(*pDChar);
			delete pDChar;
		}
	}

	m_Modified = FALSE;
}

BEGIN_MESSAGE_MAP(CDigistring, CStatic)
	//{{AFX_MSG_MAP(CDigistring)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDigistring message handlers

void CDigistring::OnPaint() 
{
	CRect rect;
	CDoubleRect CharRect;
	GetClientRect(&rect);

	CPaintDC dc(this); // device context for painting
	dc.SetBkColor(m_BackColor);
	CMemDC memDC(&dc, &rect);

	CBrush hBrushOff, hBrushOn;
	hBrushOff.CreateSolidBrush(m_OffColor);
	hBrushOn.CreateSolidBrush(m_OnColor);
	CBrush *pOldBrush = memDC.SelectObject(&hBrushOn);

		int r = int(GetRValue(m_OffColor) * 0.75 + GetRValue(m_BackColor) * 0.25);
		int g = int(GetGValue(m_OffColor) * 0.75 + GetGValue(m_BackColor) * 0.25);
		int b = int(GetBValue(m_OffColor) * 0.75 + GetBValue(m_BackColor) * 0.25);

	CPen OffPen(PS_SOLID | PS_ENDCAP_ROUND, 1, RGB(r,g,b));
		r = int(GetRValue(m_OnColor) * 0.75 + GetRValue(m_BackColor) * 0.25);
		g = int(GetGValue(m_OnColor) * 0.75 + GetGValue(m_BackColor) * 0.25);
		b = int(GetBValue(m_OnColor) * 0.75 + GetBValue(m_BackColor) * 0.25);
	CPen OnPen(PS_SOLID | PS_ENDCAP_ROUND, 1, RGB(r,g,b));
	CPen *pOldPen = memDC.SelectObject(&OffPen);

	int iTotWidth = 0;
	double dRelWidth, dRelHeight;
	// TODO: Add your message handler code here

	DigiCharVector::iterator CharIterator;

	// Calculate resizing factors...
	BuildString();
	for (CharIterator = m_CharVector.begin(); CharIterator != m_CharVector.end();
		CharIterator++)
	{
		iTotWidth += CharIterator->GetNormWidth();
	}
	dRelWidth = double(rect.Width()) / iTotWidth;
	dRelHeight = double(rect.Height()) / NORM_DIGIHEIGHT;

	// If proportional make offset for centered text
	if (m_DispStyle & DS_SZ_PROP)
	{
		if (dRelWidth < dRelHeight)
			dRelHeight = dRelWidth;
		else
			dRelWidth = dRelHeight;

		CharRect.left = (rect.Width() - dRelWidth * iTotWidth) / 2;
		CharRect.top = (rect.Height() - dRelHeight * NORM_DIGIHEIGHT) / 2; 
	}
	else
		CharRect.SetRectEmpty();

	// Draw all characters...
	for (CharIterator = m_CharVector.begin(); CharIterator != m_CharVector.end();
		CharIterator++)
	{
		CharRect.SetRect(CharRect.left, CharRect.top,
			CharRect.left + dRelWidth * CharIterator->GetNormWidth(), 
			CharRect.top  + dRelHeight * NORM_DIGIHEIGHT);

		CharIterator->Draw(&memDC, CharRect, &OffPen, &OnPen, &hBrushOff, &hBrushOn);

		CharRect.left += dRelWidth * CharIterator->GetNormWidth();
	}

	// Mama says: Clean up your mess!
	memDC.SelectObject(pOldPen);
	memDC.SelectObject(pOldBrush);
	OffPen.DeleteObject();
	OnPen.DeleteObject();
	hBrushOff.DeleteObject();
	hBrushOn.DeleteObject();
}

BOOL CDigistring::OnEraseBkgnd(CDC* /*pDC*/) 
{
	// Don't erase the background to avoid flickering
	// Background is painted in CMemDC::CMemDC(); with FillSolidRect();
	return FALSE;
}
