Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Introduction to SSE Programming

, 10 Jul 2003
An article describes programming floating-point calculations using Streaming SIMD Extensions
// Chart.cpp : implementation file
// Originator : Kris Jearakul  tuktun@hotmail.com
//				http://krisj.iwarp.com
// Comment	  : Use with your own risk !!

#include "stdafx.h"
#include "Chart.h"
#include "math.h"

#pragma warning( disable : 4312 )



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

/////////////////////////////////////////////////////////////////////////////
// CChart

CChart::CChart()
{
	SetRange(-10,10,-10,10);
	strChartTitle = "Untitled Chart" ;
	strLabelX = "x" ;
	strLabelY = "y" ;
	iChartType = 2 ;
	nGridX = 10 ;
	nGridY = 10 ;
	bIsSerieAllocated = FALSE ;
	bLogScale = FALSE ;
	bFontIsCreate = FALSE ;
	bBkNeedToUpdate = TRUE ;
	nPlotIndex = 0 ;
	nPointCount = 0 ;
	nSerieCount = 1 ;

	m_BGColor = RGB(0,255,255);
	m_AxisColor = RGB(0,0,0);
	m_GridColor = RGB(120,120,120);


}

CChart::~CChart()
{

	for( int i = 0  ; i < nSerieCount ; i++){
		mpSerie[i].FreeSerie() ;
	}
	if(memBkDC.GetSafeHdc() != NULL)
	   memBkDC.SelectObject(m_pOldBkBitmap);
	if( bFontIsCreate ) {
		delete pLegendFontY ;
		delete pLegendFontX ;
		delete pTitleFont ;
	}
}


BEGIN_MESSAGE_MAP(CChart, CWnd)
	//{{AFX_MSG_MAP(CChart)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CChart message handlers


BOOL CChart::Create(DWORD dwStyle, CRect &rect, CWnd *pParent, UINT id)
{
	//DWORD style = dwStyle & ( ~WS_BORDER ) ;
	//static CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW);
	BOOL result ;
	
	result = CWnd::CreateEx(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE, 
                          NULL, NULL, dwStyle, 
                          rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top,
                          pParent->GetSafeHwnd(), 
                          (HMENU)id) ;

	if( !result )
		AfxMessageBox("Error creating window");
	m_ctlRect = rect ;
	pParent->ClientToScreen(m_ctlRect) ;
	ScreenToClient(m_ctlRect) ;
	CreateFont(); // Get system font for label draw 
	bFontIsCreate = TRUE ;
	CalcRect() ; // Compute rectangle 
	ResCalc(); // Compute resolution per dot .

	return result ;
}

void CChart::CalcRect()
{
	int offsetX , offsetY ;

	if(m_ctlRect)
	{
		m_clientRect.left  = m_ctlRect.left+2 ;
		m_clientRect.right = m_ctlRect.right-2 ;
		m_clientRect.top   = m_ctlRect.top+2 ;
		m_clientRect.bottom = m_ctlRect.bottom-2;
	} else {
		m_clientRect = m_ctlRect ;
		}

	//Calculate offset of window to be 4/5 of its window .

	offsetY   = m_clientRect.Height()/5 ;
	offsetX   = m_clientRect.Width()/5 ;
	m_axisRect.left = m_clientRect.left + (offsetX) ;
	m_axisRect.right = m_clientRect.right - (offsetX/2)   ;
	m_axisRect.top = m_clientRect.top + (offsetY) ;
	m_axisRect.bottom = m_clientRect.bottom - (offsetY) ;
}

//////////////////////////////////////////////////////////
// Draw box and fill color inside it .
void CChart::DrawBorder(CDC *pDC)
{
	CBrush brushCtl;//(m_BGColor);
	brushCtl.CreateSolidBrush(m_BGColor);
  	pDC->Rectangle(m_clientRect);
	pDC->FillRect(m_clientRect,&brushCtl) ;
}

void CChart::DrawAxis(CDC *pDC)
{
	CPen *old , pen(PS_SOLID, 2, m_AxisColor) ;
	old = pDC->SelectObject(&pen) ;
	double dcenter ;
	switch( iChartType ) {
	
	case 0:
		pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MIN]));
		pDC->LineTo(Corrdinate(dRangeX[MAX],dRangeY[MIN]));
		pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MIN]));
		pDC->LineTo(Corrdinate(dRangeX[MIN],dRangeY[MAX]));
		break;
	
	case 1:
		dcenter = (dRangeY[MAX] + dRangeY[MIN]) / 2 ;
		pDC->MoveTo(Corrdinate(dRangeX[MIN],dcenter));
		pDC->LineTo(Corrdinate(dRangeX[MAX],dcenter));
		pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MIN]));
		pDC->LineTo(Corrdinate(dRangeX[MIN],dRangeY[MAX]));
		break;

	case 2:
		//draw vertical axis
		dcenter = (dRangeX[MAX] + dRangeX[MIN]) /2 ;
		pDC->MoveTo(Corrdinate(dcenter,dRangeY[MIN]));
		pDC->LineTo(Corrdinate(dcenter,dRangeY[MAX]));
		
		//draw horizontal axis
		dcenter = (dRangeY[MAX] + dRangeY[MIN]) /2 ;
		pDC->MoveTo(Corrdinate(dRangeX[MIN],dcenter));
		pDC->LineTo(Corrdinate(dRangeX[MAX],dcenter));
		break;

	}
	
	pDC->SelectObject(old) ;
}

CPoint CChart::Corrdinate(double x, double y)
{
	double rx , ry ;
	int xPixel , yPixel ;
	CPoint retPt ;

	rx = x - dRangeX[MIN] ; // Calculate horizontal offset from origin
	ry = y - dRangeY[MIN] ; // Calculate vertical offset from origin .

	// Convert offset to be number of pixel on screen .
	xPixel = (int)(rx / dResX) ; 
	yPixel = (int)(ry / dResY) ;

	//Calulate point to be drawn .
	retPt.x= xPixel + m_axisRect.left ;
	retPt.y= m_axisRect.bottom - yPixel; 
	return retPt ;
}

void CChart::Plot(CDC *pDC)
{
	
	// If there is no data available for plot then return .
	if ( !nPlotIndex ) return ;
		
	CPen *old , *pen ;

	
	CPoint pt ;
	int i;
	
	for( i = 0 ; i < nSerieCount ; i++) {
		// Create the new pen as the color of serie 
		pen = new CPen(PS_SOLID,0,mpSerie[i].m_plotColor);
		old = pDC->SelectObject(pen);
		// calculate the corrdinate of ploting point.
		pt = Corrdinate(mpSerie[i].dValueX[0],mpSerie[i].dValueY[0]) ;
		pDC->MoveTo(pt);
		//Start plot all available data .
		for(int index = 1 ; index <= nPlotIndex ; index++){
			pt = Corrdinate(mpSerie[i].dValueX[index],
						mpSerie[i].dValueY[index]) ;
		pDC->LineTo(pt) ;		
		}
		pDC->SelectObject(old);
		delete pen ;
	}
	
}

void CChart::DrawChartTitle(CDC *pDC)
{
	int x , y ;
	int oldbkmode ;
	CFont *old ;


	old = pDC->SelectObject(pTitleFont);
	oldbkmode = pDC->SetBkMode(TRANSPARENT) ;

	CSize textSize = pDC->GetTextExtent(strChartTitle,
		strChartTitle.GetLength()) ;

	//Calculate centre window corrdinate of text ;
	pDC->SetTextAlign(TA_LEFT);
	y = m_ctlRect.top + (textSize.cy/2) ;
	x = m_clientRect.left + 
		(m_clientRect.Width()/2) - (textSize.cx/2) ;

	pDC->TextOut(x,y,strChartTitle) ;
	pDC->SetBkMode(oldbkmode);
	pDC->SelectObject(old);
}

void CChart::DrawGrid(CDC *pDC)
{
	CPen *old , pen(PS_SOLID, 0, m_GridColor);
	CPoint m_start, m_stop ;
	int i  ;
	double x ,y ;
	double step ;

	old = pDC->SelectObject(&pen);
	
	// Draw vertical grid 

	step = (dRangeX[MAX] - dRangeX[MIN]) / (double)nGridX ;
	for( i = 0 ; i <= nGridX ; i++ )
	{
		x = dRangeX[MIN] + (step * (double)i) ;
		m_start = Corrdinate(x,dRangeY[MIN]);
		m_stop  = Corrdinate(x,dRangeY[MAX]);
		pDC->MoveTo(m_start);
		pDC->LineTo(m_stop);
	}
	
	// Draw horizontal grid.

	step = (dRangeY[MAX] - dRangeY[MIN]) / (double)nGridY ;
	for( i = 0 ; i <= nGridY ; i++ )
	{
		y = dRangeY[MIN] + (step * (double)i) ;
		m_start = Corrdinate(dRangeX[MIN],y) ;
		m_stop  = Corrdinate(dRangeX[MAX],y) ;
		pDC->MoveTo(m_start);
		pDC->LineTo(m_stop);
	}
	
	pDC->SelectObject(old);
}

BOOL CChart::SetRange(double dMinX, double dMaxX, 
					   double dMinY,double dMaxY)
{

	bBkNeedToUpdate = TRUE ; // Background need to be re-draw
	dRangeY[MIN] = dMinY ;
	dRangeY[MAX] = dMaxY ;
	dRangeX[MAX] = dMaxX ;
	dRangeX[MIN] = dMinX ;
    ResCalc();
	return TRUE ;	
}

////////////////////////////////////////////////////////
//Calculate resolution per dot
void CChart::ResCalc()
{
	double dpixelx, dpixely ;
	
	dpixelx = (double)m_axisRect.Width() ;
	dpixely = (double)m_axisRect.Height() ;
	dResY = (dRangeY[MAX] - dRangeY[MIN]) / dpixely ;
	dResX = (dRangeX[MAX] - dRangeX[MIN]) / dpixelx ;
}

///////////////////////////////////////////////////////
// Set Axis style 
//
void CChart::SetAxisStyle(int iStyle)
{
	if( (iStyle > 2) || (iStyle < 0) ) return ;
	iChartType = iStyle ;
}

////////////////////////////////////////////////////////
// Draw 1/5 fine scale in log mode
// in order to implement log grid set bLogScale = TRUE
void CChart::DrawLogGrid(CDC *pDC)
{
	// Change this number for changing number of fine scales.
	const int FINE_SCALE = 5 ; 
	CPen *old , pen(PS_SOLID, 0, RGB(192,192,192)) ;
	CPoint m_start, m_stop ;
	int i  ;
	int j ;
	double y ;
	double step ;
	double Ymax , Ymin ;

	Ymax = dRangeY[MAX] ;
	Ymin = dRangeY[MIN] ;
	//Remap scale to 0 - nGridY
	SetRange(dRangeX[MIN],dRangeX[MAX],0,nGridY);
	old = pDC->SelectObject(&pen);
	for( j = (int)dRangeY[MIN] ; j < (int)dRangeY[MAX] ; j ++) {
	   y = (double)j;
	   step = (pow(10.0,y+1) - pow(10.0,y)) /(double)FINE_SCALE ;
		for(i = 0 ; i < FINE_SCALE ; i++ )
		{
		
			y = log10(pow(10.0,y) + step) ;
			m_start = Corrdinate(dRangeX[MIN],y) ;
			m_stop  = Corrdinate(dRangeX[MAX],y) ;
			pDC->MoveTo(m_start);
			pDC->LineTo(m_stop);

		}
	}
	SetRange(dRangeX[MIN],dRangeX[MAX],Ymin,Ymax ) ;
	pDC->SelectObject(old);
	
}

////////////////////////////////////////
// Allocate memory for plotting 
BOOL CChart::AllocSerie(int nSerie)
{
	
	if( !bIsSerieAllocated )
	{
		for(int i = 0 ; i < nSerieCount ; i++ ) {
			if ( !mpSerie[i].AllocSerie(nSerie)) {
				AfxMessageBox("Can not allocate serie") ;
				return FALSE ;
			}
		}
	}
	
	nPointCount = nSerie ;
	bIsSerieAllocated = TRUE ;
	return TRUE ;

}

BOOL CChart::SetXYValue(double x, double y, int index, int iSerieIdx)
{
	if(!bIsSerieAllocated ) return FALSE ;
	if (iSerieIdx > (nSerieCount-1)) return FALSE ;

	// Prevent writting to unallocated buffer 
	if(index >= nPointCount ) return FALSE ;
	
	// Clip the ploting area if it exceed ranged .
	if(x > dRangeX[MAX] ) x = dRangeX[MAX] ;
	if(x < dRangeX[MIN] ) x = dRangeX[MIN] ;
	if(y > dRangeY[MAX] ) y = dRangeY[MAX] ;
	if(y < dRangeY[MIN] ) y = dRangeY[MIN];

	
	mpSerie[iSerieIdx].dValueX[index] = x ;
	mpSerie[iSerieIdx].dValueY[index] = y ;
	mpSerie[iSerieIdx].bIsPlotAvailable = TRUE ;

	nPlotIndex = index ;
	return TRUE ;

}


//////////////////////////////////////////////////
// Set chart title
void CChart::SetChartTitle(CString str)
{
	strChartTitle = str ;
	bBkNeedToUpdate = TRUE ; // Background need to be re-draw
}

////////////////////////////////////////////////////
// Set number of grids on x axis and y axis
void CChart::SetGridXYNumber(int gridx, int gridy)
{
	nGridX = gridx ;
	nGridY = gridy ;
	bBkNeedToUpdate = TRUE ;
}

//////////////////////////////////////////////////////
// Create desired fonts for draw legend in x axis and 
// y axis .
void CChart::CreateFont()
{
	//Create system font ..
	LOGFONT d_lf ;
	
	// Init desired font
	memset(&d_lf, 0, sizeof(LOGFONT));
	lstrcpy(d_lf.lfFaceName, "Times New Roman") ;

	// Initial font size 
	// Get a screen DC 
	CWindowDC wdc(NULL) ;
	const int cyPixels = wdc.GetDeviceCaps(LOGPIXELSY);
	d_lf.lfHeight = -1 * MulDiv(7, cyPixels, 72);

	// Create a new font 7 pts.
	pLegendFontY = new CFont() ;
	pLegendFontY->CreateFontIndirect(&d_lf);

	d_lf.lfHeight = -1 * MulDiv(12, cyPixels, 72);
	d_lf.lfWeight = FW_BOLD ;
	pTitleFont = new CFont();
	pTitleFont->CreateFontIndirect(&d_lf);


	d_lf.lfWeight = 0 ;
	d_lf.lfOrientation = 900 ; // Rotate font 90 degree for x axis
	d_lf.lfEscapement = 900 ;  
	d_lf.lfHeight = -1 * MulDiv(7, cyPixels, 72);
	pLegendFontX = new CFont() ;
	pLegendFontX->CreateFontIndirect(&d_lf);

}

void CChart::DrawGridLabel(CDC *pDC)
{
	CFont *oldFont ;

	oldFont = pDC->SelectObject(pLegendFontY);

	int i ;
	double res , y  ; 
	CPoint cal_pt ;
	CSize txtSize ;
	CString str ;

	
	res = (dRangeY[MAX] - dRangeY[MIN]) / nGridY ;
	pDC->SetTextAlign(TA_RIGHT);
	pDC->SetBkMode(TRANSPARENT);

	for ( i = 0 ; i <= nGridY ; i++)
	{
		y = dRangeY[MIN] + (res * (double)i) ;
		cal_pt = Corrdinate(dRangeX[MIN],y) ;
		str.Format("%5.2f",y) ;

		txtSize = pDC->GetTextExtent(str) ;
		cal_pt.x -= 5 ;
		cal_pt.y -= txtSize.cy/2 ;

		pDC->TextOut(cal_pt.x,cal_pt.y,str) ;
	}

	txtSize = pDC->GetTextExtent(strLabelX);
	cal_pt.x = m_ctlRect.CenterPoint().x + (txtSize.cx/2) ;
	cal_pt.y = m_ctlRect.bottom - txtSize.cy - 5;
	pDC->TextOut(cal_pt.x,cal_pt.y,strLabelX);

	pDC->SelectObject(pLegendFontX);

	res = (dRangeX[MAX] - dRangeX[MIN]) / nGridX ;
	for ( i = 0 ; i <= nGridX ; i ++ )
	{
		y = dRangeX[MIN] + ( res * (double)i);
		cal_pt = Corrdinate(y,dRangeY[MIN]);
		str.Format("%5.1f",y);

		txtSize = pDC->GetTextExtent(str) ;
		cal_pt.x -= txtSize.cy/2 ;
		cal_pt.y += 5 ;
		pDC->TextOut(cal_pt.x,cal_pt.y,str);
	
	}
	txtSize = pDC->GetTextExtent(strLabelY);
	cal_pt.x = m_ctlRect.left + (txtSize.cy/2) ;
	cal_pt.y = m_ctlRect.CenterPoint().y  - (txtSize.cx/2) ;
	pDC->TextOut(cal_pt.x,cal_pt.y,strLabelY);
	pDC->SelectObject(oldFont);

}

void CChart::ClearChart()
{
	nPlotIndex = 0 ;
	InvalidateRect(m_clientRect);
}

void CChart::PrintChart(CDC *pDC,int x , int y)
{
	
	int xPixel ;
	int yPixel  ;
	int oldmapmode ;

	CDC *dc = GetDC();
	
	xPixel = pDC->GetDeviceCaps(LOGPIXELSX);
	yPixel = pDC->GetDeviceCaps(LOGPIXELSY);
	
	//Calculate ratio to be zoomed.
	xPixel =  xPixel /dc->GetDeviceCaps(LOGPIXELSX);
	yPixel =  yPixel /dc->GetDeviceCaps(LOGPIXELSY);
	ReleaseDC(dc);
	
	oldmapmode = pDC->SetMapMode(MM_ANISOTROPIC);
	pDC->SetViewportExt(xPixel,yPixel);
	pDC->SetViewportOrg(x,y);

	DrawBorder(pDC);
	pDC->DrawEdge(m_ctlRect,BDR_SUNKENINNER|BDR_SUNKENOUTER, BF_RECT);
	DrawChartTitle(pDC);
	if ( bLogScale )
	   DrawLogGrid(pDC);
	DrawGrid(pDC);
	
	DrawAxis(pDC) ;
	DrawGridLabel(pDC);
	Plot(pDC) ;

	pDC->SetMapMode(oldmapmode);
}

void CChart::SetChartLabel(CString strX, CString strY)
{
	strLabelX = strX ;
	strLabelY = strY ;
}


void CChart::OnPaint() 
{
	CPaintDC dc(this); // device context for painting

	CDC memPlotDC ;
	CBitmap *oldBitmap ;
	CBitmap m_plotBitmap ;
	
	// Check if background need to be redrawn
	if (bBkNeedToUpdate){
		DrawBackGround(&dc);
		bBkNeedToUpdate = FALSE ;
	}
	memPlotDC.CreateCompatibleDC(&dc) ;
	m_plotBitmap.CreateCompatibleBitmap(&dc,
		m_clientRect.Width(), m_clientRect.Height());
	oldBitmap = (CBitmap*)memPlotDC.SelectObject(&m_plotBitmap);
	
	// BitBlt background .
	memPlotDC.BitBlt(0,0, m_clientRect.Width(), m_clientRect.Height(),
		&memBkDC,0,0,SRCCOPY);
	Plot(&memPlotDC);

	dc.BitBlt(0,0, m_clientRect.Width(), m_clientRect.Height(),
		&memPlotDC,0,0,SRCCOPY);
	memPlotDC.SelectObject(oldBitmap);

}

void CChart::DrawBackGround(CDC *pDC)
{

		
	if(memBkDC.GetSafeHdc() == NULL) {
		memBkDC.CreateCompatibleDC(pDC);
		m_BkBitmap.CreateCompatibleBitmap(pDC,
			m_clientRect.Width(), m_clientRect.Height());
	}
	

	m_pOldBkBitmap = (CBitmap*)memBkDC.SelectObject(&m_BkBitmap) ;
	DrawBorder(&memBkDC);
	DrawAxis(&memBkDC);
	DrawGrid(&memBkDC);
	if(bLogScale)
		DrawLogGrid(&memBkDC);
	DrawGridLabel(&memBkDC);
	DrawChartTitle(&memBkDC);

}

void CChart::Invalidate(BOOL bErase )
{
	if ( bErase )
		bBkNeedToUpdate = TRUE ;

	CWnd::Invalidate(bErase) ;
}



////////////////////////////////////////////////////
// Source of CPlotSerie 

CPlotSerie::CPlotSerie() 
{
		dValueX = NULL ;
		dValueY = NULL ;
		bIsPlotAvailable = FALSE ;
		bBufferAllocated = FALSE ;
		m_plotColor = RGB(0,0,0);
}

BOOL CPlotSerie::AllocSerie(UINT nPoint)
{
	dValueX = (double*)malloc(nPoint * sizeof (double) ) ;
	dValueY = (double*)malloc(nPoint * sizeof (double) ) ;
	if ( (dValueX == NULL) || (dValueY == NULL) ) 
		return FALSE ;
	bBufferAllocated = TRUE ;
	return TRUE ;
}

BOOL CPlotSerie::FreeSerie()
{
	if ( bBufferAllocated ) {
		free(dValueX) ;
		free(dValueY) ;
	}
	bBufferAllocated = FALSE ;
	return TRUE ;
}


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 The Code Project Open License (CPOL)

Share

About the Author

Alex Fr
Software Developer
Israel Israel
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140821.2 | Last Updated 11 Jul 2003
Article Copyright 2003 by Alex Fr
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid