Click here to Skip to main content
15,886,137 members
Articles / Desktop Programming / MFC

2D Graph ActiveX Control

Rate me:
Please Sign up or sign in to vote.
4.89/5 (159 votes)
5 Aug 2003MIT8 min read 1.5M   70.2K   339  
An ActiveX control for 2D data visualisation
// NTGraphCtl.cpp : Implementation of the CNTGraphCtrl ActiveX Control class.


#include "stdafx.h"
#include "NTGraph.h"
#include "NTGraphCtl.h"
#include "NTGraphPpg.h"

#include <math.h>

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


IMPLEMENT_DYNCREATE(CNTGraphCtrl, COleControl)


/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CNTGraphCtrl, COleControl)
	//{{AFX_MSG_MAP(CNTGraphCtrl)
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
	ON_MESSAGE(OCM_COMMAND, OnOcmCommand)
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// Dispatch map

BEGIN_DISPATCH_MAP(CNTGraphCtrl, COleControl)
	//{{AFX_DISPATCH_MAP(CNTGraphCtrl)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "ShowGrid", m_showGrid, OnShowGridChanged, VT_BOOL)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "ChangeMappingMode", m_changeMappingMode, OnChangeMappingModeChanged, VT_BOOL)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "XLabel", m_strLabelX, OnXLabelChanged, VT_BSTR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "YLabel", m_strLabelY, OnYLabelChanged, VT_BSTR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "GridColor", m_gridColor, OnGridColorChanged, VT_COLOR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "LabelColor", m_labelColor, OnLabelColorChanged, VT_COLOR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "AxisColor", m_axisColor, OnAxisColorChanged, VT_COLOR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "NGridX", m_nGridX, OnNGridXChanged, VT_I2)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "NGridY", m_nGridY, OnNGridYChanged, VT_I2)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "MinX", m_minX, OnMinXChanged, VT_R8)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "MaxX", m_maxX, OnMaxXChanged, VT_R8)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "MinY", m_minY, OnMinYChanged, VT_R8)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "MaxY", m_maxY, OnMaxYChanged, VT_R8)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "ShowCursor", m_showCursor, OnShowCursorChanged, VT_BOOL)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "GraphTitle", m_strGraphTitle, OnGraphTitleChanged, VT_BSTR)
	DISP_PROPERTY_NOTIFY(CNTGraphCtrl, "CursorColor", m_cursorColor, OnCursorColorChanged, VT_COLOR)
	DISP_PROPERTY_EX(CNTGraphCtrl, "PlotColor", GetPlotColor, SetPlotColor, VT_COLOR)
	DISP_PROPERTY_EX(CNTGraphCtrl, "Element", GetElement, SetElement, VT_I2)
	DISP_PROPERTY_EX(CNTGraphCtrl, "ElementCount", GetElementCount, SetElementCount, VT_I2)
	DISP_FUNCTION(CNTGraphCtrl, "ClearGraph", ClearGraph, VT_EMPTY, VTS_NONE)
	DISP_FUNCTION(CNTGraphCtrl, "CreateElement", CreateElement, VT_BOOL, VTS_I4)
	DISP_FUNCTION(CNTGraphCtrl, "SetRange", SetRange, VT_BOOL, VTS_R8 VTS_R8 VTS_R8 VTS_R8)
	DISP_FUNCTION(CNTGraphCtrl, "SetXYValue", SetXYValue, VT_BOOL, VTS_R8 VTS_R8 VTS_I4 VTS_I2)
	DISP_STOCKPROP_APPEARANCE()
	DISP_STOCKPROP_BACKCOLOR()
	DISP_STOCKPROP_BORDERSTYLE()
	DISP_STOCKPROP_ENABLED()
	DISP_STOCKPROP_CAPTION()
	//}}AFX_DISPATCH_MAP
	DISP_FUNCTION_ID(CNTGraphCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()


/////////////////////////////////////////////////////////////////////////////
// Event map

BEGIN_EVENT_MAP(CNTGraphCtrl, COleControl)
	//{{AFX_EVENT_MAP(CNTGraphCtrl)
	EVENT_STOCK_CLICK()
	EVENT_STOCK_MOUSEMOVE()
	//}}AFX_EVENT_MAP
END_EVENT_MAP()


/////////////////////////////////////////////////////////////////////////////
// Property pages

// TODO: Add more property pages as needed.  Remember to increase the count!
BEGIN_PROPPAGEIDS(CNTGraphCtrl, 2)
	PROPPAGEID(CNTGraphPropPage::guid)
	PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CNTGraphCtrl)


/////////////////////////////////////////////////////////////////////////////
// Initialize class factory and guid

IMPLEMENT_OLECREATE_EX(CNTGraphCtrl, "NTGRAPH.NTGraphCtrl.1",
	0x14f24365, 0x6c9d, 0x419e, 0x89, 0x94, 0xe7, 0xc9, 0x17, 0x9c, 0xa3, 0x4f)


/////////////////////////////////////////////////////////////////////////////
// Type library ID and version

IMPLEMENT_OLETYPELIB(CNTGraphCtrl, _tlid, _wVerMajor, _wVerMinor)


/////////////////////////////////////////////////////////////////////////////
// Interface IDs

const IID BASED_CODE IID_DNTGraph =
		{ 0x3b090608, 0x7603, 0x486d, { 0x9e, 0x9c, 0x9f, 0x26, 0x86, 0x4e, 0x13, 0xa0 } };
const IID BASED_CODE IID_DNTGraphEvents =
		{ 0x35000290, 0x97bd, 0x4b15, { 0xba, 0x89, 0xf4, 0xd, 0x82, 0x24, 0xdd, 0xd9 } };


/////////////////////////////////////////////////////////////////////////////
// Control type information

static const DWORD BASED_CODE _dwNTGraphOleMisc =
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CNTGraphCtrl, IDS_NTGRAPH, _dwNTGraphOleMisc)


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::CNTGraphCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CNTGraphCtrl

BOOL CNTGraphCtrl::CNTGraphCtrlFactory::UpdateRegistry(BOOL bRegister)
{
	// TODO: Verify that your control follows apartment-model threading rules.
	// Refer to MFC TechNote 64 for more information.
	// If your control does not conform to the apartment-model rules, then
	// you must modify the code below, changing the 6th parameter from
	// afxRegApartmentThreading to 0.

	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_NTGRAPH,
			IDB_NTGRAPH,
			afxRegApartmentThreading,
			_dwNTGraphOleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::CNTGraphCtrl - Constructor

CNTGraphCtrl::CNTGraphCtrl()
{
	InitializeIIDs(&IID_DNTGraph, &IID_DNTGraphEvents);

	// TODO: Initialize your control's instance data here.
	m_minX=m_minY=0;
	m_maxX=m_maxY=10;
	SetRange(0,10,0,10);
 	m_strLabelX = "X Axis" ;
	m_strLabelY = "Y Axis" ;
	m_strGraphTitle = "XY Graph";
	m_nGridX = 5 ;
	m_nGridY = 5;
	bIsElementAllocated = FALSE ;
	bFontIsCreate = FALSE ;
	nPlotIndex = 0 ;
	nPointCount = 2000 ;
	nElementCount = 2 ;
	m_ElementId=0;
    
	m_BGColor    = RGB(255,255,255);
	m_axisColor  = RGB(0,0,0);
	m_labelColor = RGB(0,0,0);
	m_gridColor  = RGB(120,120,120);
    m_cursorColor = RGB(255,0,0);

}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::~CNTGraphCtrl - Destructor

CNTGraphCtrl::~CNTGraphCtrl()
{
	// TODO: Cleanup your control's instance data here.
	for( int i = 0  ; i < nElementCount ; i++){
		mpElement[i].FreeElement() ;
	}
	if( bFontIsCreate ) {
		delete pTickFont ;
		delete pXLabelFont;
		delete pYLabelFont;
		delete pTitleFont ;
	}
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::OnDraw - Drawing function

void CNTGraphCtrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
   
	DoSuperclassPaint(pdc, rcBounds);

    m_pDC = pdc;
    m_ctlRect = rcBounds;
    m_clientRect = rcBounds;

	PrepareForDrawing(&rcBounds);

 	CBrush hbrBackground(TranslateColor(GetBackColor()));
	pdc->FillRect (m_ctlRect,&hbrBackground);

    DrawAxis() ;
    DrawGraphTitle();
	DrawAxisLabel();

	if (m_showGrid)
			DrawGrid();
	else 
			DrawTicks();

	DrawGraphTitle();

	PlotElement() ;

	if (m_showCursor)
		DrawCursor();
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::DoPropExchange - Persistence support

void CNTGraphCtrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: Call PX_ functions for each persistent custom property.
    PX_Bool(pPX,_T("ShowGrid"),m_showGrid,FALSE);
	PX_Bool(pPX,_T("ChangeMappingMode"),m_changeMappingMode,FALSE);
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::GetControlFlags -
// Flags to customize MFC's implementation of ActiveX controls.
//
// For information on using these flags, please see MFC technical note
// #nnn, "Optimizing an ActiveX Control".
DWORD CNTGraphCtrl::GetControlFlags()
{
	DWORD dwFlags = COleControl::GetControlFlags();


	// The control will not be redrawn when making the transition
	// between the active and inactivate state.
	dwFlags |= noFlickerActivate;
	return dwFlags;
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::OnResetState - Reset control to default state

void CNTGraphCtrl::OnResetState()
{
	COleControl::OnResetState();  // Resets defaults found in DoPropExchange

	// TODO: Reset any other control state here.
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::AboutBox - Display an "About" box to the user

void CNTGraphCtrl::AboutBox()
{
	CDialog dlgAbout(IDD_ABOUTBOX_NTGRAPH);
	dlgAbout.DoModal();
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::PreCreateWindow - Modify parameters for CreateWindowEx

BOOL CNTGraphCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
	cs.lpszClass = _T("STATIC");
	return COleControl::PreCreateWindow(cs);
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::IsSubclassedControl - This is a subclassed control

BOOL CNTGraphCtrl::IsSubclassedControl()
{
	return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl::OnOcmCommand - Handle command messages

LRESULT CNTGraphCtrl::OnOcmCommand(WPARAM wParam, LPARAM lParam)
{
#ifdef _WIN32
	WORD wNotifyCode = HIWORD(wParam);
#else
	WORD wNotifyCode = HIWORD(lParam);
#endif

	// TODO: Switch on wNotifyCode here.

	return 0;
}


/////////////////////////////////////////////////////////////////////////////
// CNTGraphCtrl message handlers

void CNTGraphCtrl::PrepareForDrawing(CRect rect)
{

    CWnd* pParent = GetParent( );     
	m_ctlRect = rect ;

	m_BGColor=TranslateColor(GetBackColor());


	if (m_changeMappingMode) {
		m_pDC->SetMapMode(MM_HIMETRIC);
		m_pDC->SetMapMode(MM_ANISOTROPIC);
		m_pDC->SetWindowExt (400,300);
	}
	m_pDC->SetViewportExt (rect.right , rect.bottom );
	m_pDC->DPtoLP(&m_ctlRect);
 
	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;
}


CPoint CNTGraphCtrl::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 CNTGraphCtrl::CalcRect()
{
	int offset;

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


	offset = 20;
    
	CSize txtXLabelSize,txtYLabelSize,txtTitleSize ;
	CString str ;
   	CPaintDC dc(this); // device context for painting
	CFont *oldFont ;
	oldFont = dc.SelectObject(pTickFont);


    int bigY = __max(abs((int)dRangeY[MAX]),abs((int)dRangeY[MIN]));
	str.Format("%g",(double)bigY);
	txtYLabelSize = dc.GetTextExtent(str);
	str.Format("%g",dRangeX[MAX]);
	txtXLabelSize = dc.GetTextExtent(str);
	
	dc.SelectObject (pTitleFont);
	txtTitleSize = dc.GetTextExtent (m_strLabelX);
	dc.SelectObject(oldFont);


	m_axisRect.left = m_clientRect.left +(offset)+txtTitleSize.cy+txtYLabelSize.cx+10;
	m_axisRect.right = m_clientRect.right - (offset/2) - (txtXLabelSize.cx/2);
	m_axisRect.top = m_clientRect.top + (offset/2) + txtTitleSize.cy + 10 ;
	m_axisRect.bottom = m_clientRect.bottom - (offset/2)-(txtTitleSize.cy+txtXLabelSize.cy)+10; 

}
////////////////////////////////////////////////////////
//Calculate resolution per dot
void CNTGraphCtrl::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 ;
}

//////////////////////////////////////////////////////
// Create desired fonts for draw legend in x axis and 
// y axis .
void CNTGraphCtrl::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(8, cyPixels, 72);

	// Create a new font 8 pts. for Ticks
	pTickFont = new CFont() ;
	pTickFont->CreateFontIndirect(&d_lf);

	d_lf.lfHeight = -1 * MulDiv(12, cyPixels, 72);
	d_lf.lfWeight = FW_BLACK ;
	
	// Create a new font 12 pts. for Title
	pTitleFont = new CFont();
	pTitleFont->CreateFontIndirect(&d_lf);
	
	// Create a new font 12 pts. for Axis Labels
	d_lf.lfWeight = FW_BOLD ;

    
	pXLabelFont = new CFont() ;
	pXLabelFont->CreateFontIndirect(&d_lf);

	// Rotate font 90 degree for Y axis label
	d_lf.lfOrientation = 900 ; 
	d_lf.lfEscapement = 900 ;
	
	pYLabelFont = new CFont() ;
	pYLabelFont->CreateFontIndirect(&d_lf);

}

void CNTGraphCtrl::DrawAxis()
{

	m_axisRect.NormalizeRect ();
	m_pDC->FillSolidRect (m_axisRect,m_BGColor);

	CPen *old , pen(PS_SOLID, 2, m_axisColor);
	
	old = m_pDC->SelectObject(&pen) ;
		m_pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MIN]));
		m_pDC->LineTo(Corrdinate(dRangeX[MAX],dRangeY[MIN]));
		m_pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MIN]));
		m_pDC->LineTo(Corrdinate(dRangeX[MIN],dRangeY[MAX]));
        m_pDC->MoveTo(Corrdinate(dRangeX[MAX],dRangeY[MIN]));
		m_pDC->LineTo(Corrdinate(dRangeX[MAX],dRangeY[MAX]));
		m_pDC->MoveTo(Corrdinate(dRangeX[MIN],dRangeY[MAX]));
		m_pDC->LineTo(Corrdinate(dRangeX[MAX],dRangeY[MAX]));
	m_pDC->SelectObject(old) ;
}

void CNTGraphCtrl::DrawGraphTitle()
{
	int x , y ;
	int oldbkmode ;
	CFont *old ;

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

	CSize textSize = m_pDC->GetTextExtent(m_strGraphTitle,
		m_strGraphTitle.GetLength()) ;

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

	m_pDC->TextOut(x,y,m_strGraphTitle) ;
	m_pDC->SetBkMode(oldbkmode);
	m_pDC->SelectObject(old);
}

void CNTGraphCtrl::DrawGrid()
{
	CPen *old , pen(PS_SOLID, 0, m_gridColor);
	CPoint m_start, m_stop ;
	int i  ;
	double x ,y ;
	double step ;

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

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

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


BOOL CNTGraphCtrl::SetRange(double MinX, double MaxX, double MinY,double MaxY)
{

	dRangeY[MIN] = MinY ;
	dRangeY[MAX] = MaxY ;
	dRangeX[MAX] = MaxX ;
	dRangeX[MIN] = MinX ;
    ResCalc();
	InvalidateControl();
	return TRUE ;	
}


void CNTGraphCtrl::DrawAxisLabel()
{
	CFont *oldFont ;

	oldFont = m_pDC->SelectObject(pTickFont);

	m_pDC->SetTextColor (m_labelColor);

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

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

	// Draw X Tick Labels
	for ( i = 0 ; i <= m_nGridY ; i++)
	{
		y = dRangeY[MIN] + (res * (double)i) ;
		cal_pt = Corrdinate(dRangeX[MIN],y) ;
		str.Format("%g",y) ;

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

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


    // Draw X Label
	m_pDC->SelectObject(pXLabelFont);
	txtSize = m_pDC->GetTextExtent(m_strLabelX);
    cal_pt.x = m_axisRect.CenterPoint().x + (txtSize.cx/2); 
	cal_pt.y = m_ctlRect.bottom - (txtSize.cy);
    
	m_pDC->TextOut(cal_pt.x,cal_pt.y,m_strLabelX);

	// Draw Y Tick Labels
   	m_pDC->SelectObject(pTickFont);
	res = (dRangeX[MAX] - dRangeX[MIN]) / m_nGridX ;

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

		txtSize = m_pDC->GetTextExtent(str) ;
		cal_pt.x += txtSize.cx/2;
		cal_pt.y += 5 ;

		m_pDC->TextOut(cal_pt.x,cal_pt.y,str);
	}
	 
		
	// Draw Y Label
	m_pDC->SelectObject(pYLabelFont);
	txtSize = m_pDC->GetTextExtent(m_strLabelY);
	cal_pt.x = m_ctlRect.left + (txtSize.cy/2);
	cal_pt.y = m_ctlRect.CenterPoint().y  - (txtSize.cx/2);
	m_pDC->TextOut(cal_pt.x,cal_pt.y,m_strLabelY);
	m_pDC->SelectObject(oldFont);

}


////////////////////////////////////////////////////////
// Draw 1/5 fine scale in log mode
// in order to implement log grid set bLogScale = TRUE
void CNTGraphCtrl::DrawTicks()
{

	CPen *old , pen(PS_SOLID, 0, m_gridColor);
	CPoint m_start, m_stop ;
	int i  ;
	double x ,y ;
	double step ;

	old = m_pDC->SelectObject(&pen);
	
	// Draw vertical Major Ticks 

	step = (dRangeX[MAX] - dRangeX[MIN]) / (double)m_nGridX ;
	for( i = 0 ; i <= m_nGridX ; i++ )
	{
		x = dRangeX[MIN] + (step * (double)i) ;
		m_start = Corrdinate(x,dRangeY[MIN]);
		m_stop = m_start; m_stop.y -= 5;	// Major Ticks on Botom
		m_pDC->MoveTo(m_start);
		m_pDC->LineTo(m_stop);
	}

	for( i = 0 ; i <= m_nGridX ; i++ )
	{
		x = dRangeX[MIN] + (step * (double)i) ;
		m_start = Corrdinate(x,dRangeY[MAX]);
		m_stop = m_start; m_stop.y += 5;	// Major Ticks on Top
		m_pDC->MoveTo(m_start);
		m_pDC->LineTo(m_stop);
	}
	

	// Draw horizontal Major Ticks
	step = (dRangeY[MAX] - dRangeY[MIN]) / (double)m_nGridY ;
	for( i = 0 ; i <= m_nGridY ; i++ )
	{
		y = dRangeY[MIN] + (step * (double)i) ;
		m_start = Corrdinate(dRangeX[MIN],y) ;
		m_stop  = m_start; m_stop.x += 5;	 // Major Ticks on Left
		m_pDC->MoveTo(m_start);
		m_pDC->LineTo(m_stop);
	}
	
	for( i = 0 ; i <= m_nGridY ; i++ )
	{
		y = dRangeY[MIN] + (step * (double)i) ;
		m_start = Corrdinate(dRangeX[MAX],y) ;
		m_stop  = m_start; m_stop.x -= 5;	// Major Ticks on Right
		m_pDC->MoveTo(m_start);
		m_pDC->LineTo(m_stop);
	}

	m_pDC->SelectObject(old);
}

BOOL CNTGraphCtrl::SetXYValue(double x, double y, long index, short iElementId)
{
	if(!bIsElementAllocated ) return FALSE ;
	if (iElementId > (nElementCount-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];

	
	mpElement[iElementId].dValueX[index] = x ;
	mpElement[iElementId].dValueY[index] = y ;
	mpElement[iElementId].bIsPlotAvailable = TRUE ;

	nPlotIndex = index ;
	return TRUE ;

}

void CNTGraphCtrl::ClearGraph()
{
	nPlotIndex = 0 ;
	InvalidateRect(m_clientRect);
}

////////////////////////////////////////
// Allocate memory for plotting 
BOOL CNTGraphCtrl::CreateElement(long nElement)
{
	
	if( !bIsElementAllocated )
	{
		for(int i = 0 ; i < nElementCount ; i++ ) {
			if ( !mpElement[i].AllocElement(nElement)) {
				AfxMessageBox("Can not allocate element!") ;
				return FALSE ;
			}
		}
	}
	
	nPointCount = nElement ;
	bIsElementAllocated = TRUE ;
	return TRUE ;

}

void CNTGraphCtrl::PlotElement()
{
	
	// If there is no data available for plot then return .
	if ( !nPlotIndex ) return ;
		
	CPen *old , *pen ;
	
	CPoint pt ;
	int i;
	
	for( i = 0 ; i < nElementCount ; i++) {
		// Create the new pen as the color of serie 
		pen = new CPen(PS_SOLID,0,mpElement[i].m_plotColor);
		old = m_pDC->SelectObject(pen);
		// calculate the corrdinate of ploting point.
		pt = Corrdinate(mpElement[i].dValueX[0],mpElement[i].dValueY[0]) ;
		m_pDC->MoveTo(pt);
		//Start plot all available data .
		for(int index = 1 ; index <= nPlotIndex ; index++){
			pt = Corrdinate(mpElement[i].dValueX[index],
						mpElement[i].dValueY[index]) ;
		m_pDC->LineTo(pt) ;		
		}
		m_pDC->SelectObject(old);
		delete pen ;
	}
	
}






////////////////////////////////////////////////////
// Source of CGraphElement 

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

BOOL CGraphElement::AllocElement(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 CGraphElement::FreeElement()
{
	if ( bBufferAllocated ) {
		free(dValueX) ;
		free(dValueY) ;
	}
	bBufferAllocated = FALSE ;
	return TRUE ;
}




void CNTGraphCtrl::OnShowGridChanged() 
{
	// TODO: Add notification handler code
    InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnChangeMappingModeChanged() 
{
	// TODO: Add notification handler code
    InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnXLabelChanged() 
{
	// TODO: Add notification handler code
    InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnYLabelChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnGridColorChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnLabelColorChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnAxisColorChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnNGridXChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnNGridYChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}




void CNTGraphCtrl::OnMinXChanged() 
{
	// TODO: Add notification handler code
	SetRange(m_minX,m_maxX,m_minY,m_maxY);
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnMaxXChanged() 
{
	// TODO: Add notification handler code
   	SetRange(m_minX,m_maxX,m_minY,m_maxY);
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnMinYChanged() 
{
	// TODO: Add notification handler code
	SetRange(m_minX,m_maxX,m_minY,m_maxY);
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnMaxYChanged() 
{
	// TODO: Add notification handler code
	SetRange(m_minX,m_maxX,m_minY,m_maxY);
	InvalidateControl();
	SetModifiedFlag();
}


OLE_COLOR CNTGraphCtrl::GetPlotColor() 
{
	// TODO: Add your property handler here
    
	return mpElement[m_ElementId].m_plotColor;
}

void CNTGraphCtrl::SetPlotColor(OLE_COLOR nNewValue) 
{
	// TODO: Add your property handler here

	mpElement[m_ElementId].m_plotColor = nNewValue;
	InvalidateControl();
	SetModifiedFlag();
}

short CNTGraphCtrl::GetElement() 
{
	// TODO: Add your property handler here
    return m_ElementId;
}

void CNTGraphCtrl::SetElement(short nNewValue) 
{
	// TODO: Add your property handler here
    m_ElementId = nNewValue;
	if (m_ElementId <0) 
		m_ElementId=0;
	SetModifiedFlag();
}

short CNTGraphCtrl::GetElementCount() 
{
	// TODO: Add your property handler here
    
	return nElementCount;
}

void CNTGraphCtrl::SetElementCount(short nNewValue) 
{
	// TODO: Add your property handler here
    nElementCount = nNewValue;
	if (nElementCount <0) 
		nElementCount=0;
	SetModifiedFlag();
}






void CNTGraphCtrl::OnShowCursorChanged() 
{
	// TODO: Add notification handler code
	InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
     double rx,ry;
		rx = (double)((point.x - m_axisRect.left)*dResX)   + dRangeX[MIN];
		ry = (double)((m_axisRect.bottom - point.y)*dResY) + dRangeY[MIN];


	if ( point.x < m_axisRect.right && 
		 point.x > m_axisRect.left && 
		 point.y > m_axisRect.top &&
		 point.y < m_axisRect.bottom )
	{

		m_dXCur = rx;
		m_dYCur = ry;
		
		m_bIsCursorInRange=TRUE;
		return;
    }
    else
	{
		m_bIsCursorInRange=FALSE;
		return;
	}
	COleControl::OnMouseMove(nFlags, point);
}

void CNTGraphCtrl::DrawCursor()
{

	if(m_bIsCursorInRange) {
		m_axisRect.NormalizeRect ();

		CPen *old , pen(PS_SOLID, 2, m_cursorColor);
	
		old = m_pDC->SelectObject(&pen) ;
			m_pDC->MoveTo(Corrdinate(m_dXCur,dRangeY[MIN]));
			m_pDC->LineTo(Corrdinate(m_dXCur,dRangeY[MAX]));
			m_pDC->MoveTo(Corrdinate(dRangeX[MIN],m_dYCur));
			m_pDC->LineTo(Corrdinate(dRangeX[MAX],m_dYCur));
  		m_pDC->SelectObject(old) ;
	}
}

void CNTGraphCtrl::OnGraphTitleChanged() 
{
	// TODO: Add notification handler code
    InvalidateControl();
	SetModifiedFlag();
}

void CNTGraphCtrl::OnCursorColorChanged() 
{
	// TODO: Add notification handler code

	SetModifiedFlag();
}


void CNTGraphCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
    InvalidateControl();
	COleControl::OnLButtonDown(nFlags, point);
}

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 MIT License


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions