Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / MFC

Plot Graphic Library

Rate me:
Please Sign up or sign in to vote.
4.95/5 (70 votes)
7 May 2003LGPL36 min read 1.4M   51.3K   383  
A library to plot data (lines, maps...) in MFC projects
In this article, you will see a library called PGL that encapsulates plot capabilities in a MFC project for VC6 and VC7. It can easily plot data generated in a project without the need of any external software.
// AxeGL.cpp: implementation of the CPGLAxe2D class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PGL/PGLAxe.h"

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

#include <float.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

IMPLEMENT_SERIAL(CPGLAxe2D, CPGLObject,1);


void CPGLAxe2D::Serialize(CArchive &archive)
{
    // call base class function first
    // base class is CObject in this case
    CPGLObject::Serialize( archive );
	int i;
	for (i=0;i<4;i++)
	{
		m_pLabels[i].Serialize(archive);
		m_pNumbers[i].Serialize(archive);
	}

	for (i=0;i<2;i++)
	{
	    if( archive.IsStoring() )
			archive << m_pStarts[i] << m_pEnds[i] 
			<< m_pSteps[i] << m_pAutoSteps[i] << m_pGridVisible[i]<<m_pLogAxis[i];
	    else
			archive >> m_pStarts[i] >> m_pEnds[i] 
			>> m_pSteps[i] >> m_pAutoSteps[i] >> m_pGridVisible[i]>>m_pLogAxis[i];
	}

	if( archive.IsStoring() )
	{
	}
	else
	{
	}
}

#ifdef _DEBUG
void CPGLAxe2D::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLObject::Dump( dc );

	// now dumping..
	dc << _T("--- CPGLAxe2D ID ") << GetID() <<_T(" --- ")<< endl;
	dc <<_T("Bounding box : bottom : ")<<m_pStarts[1]<<
		_T(" top : ")<<m_pEnds[1]<<
		_T(" left : ")<<m_pStarts[0]<<
		_T(" right : ")<<m_pEnds[0]<<endl;
}

void CPGLAxe2D::AssertValid() const
{
    // call inherited AssertValid first
    CPGLObject::AssertValid();

	ASSERT_VALID(m_pView);
	for (int i=0;i<4;i++)
	{
		m_pLabels[i].AssertValid();
	}
} 
#endif

CPGLAxe2D::CPGLAxe2D(CPGLView2D* pView)
: CPGLObject()
{
	SetColor(0.0,0.0,0.0,1.0f);
	for (int i=0;i<2;i++)
	{
		m_pStarts[i]=0.0;
		m_pEnds[i]=1.0;
		m_pAutoSteps[i]=TRUE;
		// setting m_pSteps
		GenerateSteps(i);
		m_pGridVisible[i]=TRUE;
		m_pLogAxis[i]=FALSE;
	}

	m_pView=pView;

	m_bTimeLabel = FALSE;
	m_tTimeLabelOrig = CTime::GetCurrentTime();
	m_tsDt = CTimeSpan(0,1,0,0);
	m_sTimeLabel = "%H";

	SetName("Axis");

	m_rBorders.left=40;
	m_rBorders.right=40;
	m_rBorders.top=40;
	m_rBorders.bottom=40;

	m_pLabels[0]=CPGLText(_T("x"));
	m_pLabels[0].SetName("Bottom label");
	m_pLabels[0].SetAlign(PGL_TEXT_ALIGN_CENTER);

	m_pLabels[1]=CPGLText(_T("y"));
	m_pLabels[1].SetName("Left label");
	m_pLabels[1].SetAngle(90);
	m_pLabels[1].SetAlign(PGL_TEXT_ALIGN_CENTER);

	m_pLabels[2]=CPGLText(_T("y"));
	m_pLabels[2].SetName("Right label");
	m_pLabels[2].SetAngle(90);
	m_pLabels[2].SetAlign(PGL_TEXT_ALIGN_CENTER);
	m_pLabels[2].Hide();

	m_pLabels[3]=CPGLText(_T("Title"));
	m_pLabels[3].SetName("Top label");
	m_pLabels[3].SetAlign(PGL_TEXT_ALIGN_CENTER);

	// numbering
	m_pNumbers[0]=CPGLText(_T(""));
	m_pNumbers[0].SetName("Bottom numbering");
	m_pNumbers[0].SetAlign(PGL_TEXT_ALIGN_CENTER);
	m_pNumberPos[0]=0;

	m_pNumbers[1]=CPGLText(_T(""));
	m_pNumbers[1].SetName("Left Numbering");
	m_pNumbers[1].SetAlign(PGL_TEXT_ALIGN_RIGHT);
	m_pNumberPos[1]=0;

	m_pNumbers[2]=CPGLText(_T(""));
	m_pNumbers[2].SetName("Top Numbering");
	m_pNumbers[2].SetAlign(PGL_TEXT_ALIGN_LEFT);
	m_pNumbers[2].Hide();
	m_pNumberPos[2]=1;

	m_pNumbers[3]=CPGLText(_T(""));
	m_pNumbers[3].SetName("Top Numbering");
	m_pNumbers[3].SetAlign(PGL_TEXT_ALIGN_CENTER);
	m_pNumbers[3].Hide();
	m_pNumberPos[3]=1;

	for (i=0;i<4;i++)
	{
		m_pMainTicksSize[i]=8;
		m_pSecondTicksSize[i]=6;
		m_pSecondTicksNb[i]=1;
	}

	// saving old ressource.
	HINSTANCE hOldInst=AfxGetResourceHandle();
	// setting dll ressource
	AfxSetResourceHandle(AfxGetInstanceHandle());
	// creating bitmap
	m_bmp.LoadBitmap(IDB_PGL_AXE2D_BITMAP);
	// restoring old ressource.
	AfxSetResourceHandle(hOldInst);
}

CPGLAxe2D::CPGLAxe2D(const CPGLAxe2D& a)
: CPGLObject(a)
{
	int i;

	for (i=0;i<4;i++)
	{
		m_pLabels[i]=a.m_pLabels[i];
		m_pNumbers[i]=a.m_pNumbers[i];
		m_pNumberPos[i]=a.m_pNumberPos[i];
		m_pMainTicksSize[i]=a.m_pMainTicksSize[i];
		m_pSecondTicksSize[i]=a.m_pSecondTicksSize[i];
		m_pSecondTicksNb[i]=a.m_pSecondTicksNb[i];
	}

	// copy the operand
	for (i=0;i<2;i++)
	{
		m_pStarts[i]=a.m_pStarts[i];
		m_pEnds[i]=a.m_pEnds[i];
		m_pSteps[i]=a.m_pSteps[i];
		m_pAutoSteps[i]=a.m_pAutoSteps[i];
		m_pGridVisible[i]=a.m_pGridVisible[i];
		m_pLogAxis[i]=a.m_pLogAxis[i];
	}

	m_rBorders=a.m_rBorders;

	m_pView=a.m_pView;

	m_bTimeLabel = a.m_bTimeLabel;
	m_tsDt = a.m_tsDt;
	m_tTimeLabelOrig = a.m_tTimeLabelOrig;
	m_sTimeLabel = a.m_sTimeLabel;
	
	// saving old ressource.
	HINSTANCE hOldInst=AfxGetResourceHandle();
	// setting dll ressource
	AfxSetResourceHandle(AfxGetInstanceHandle());
	// creating bitmap
	m_bmp.LoadBitmap(IDB_PGL_AXE2D_BITMAP);
	// restoring old ressource.
	AfxSetResourceHandle(hOldInst);
}

CPGLAxe2D& CPGLAxe2D::operator=(const CPGLAxe2D& a)
{
	int i;
	// prevent self-assignement
	if (&a != this)
	{
		// invoke CPGLObject copy assignement operator
		this->CPGLObject::operator =(a);

		// copy the operand
		for (i=0;i<4;i++)
		{
			m_pLabels[i]=a.m_pLabels[i];
			m_pNumbers[i]=a.m_pNumbers[i];
			m_pNumberPos[i]=a.m_pNumberPos[i];
			m_pMainTicksSize[i]=a.m_pMainTicksSize[i];
			m_pSecondTicksSize[i]=a.m_pSecondTicksSize[i];
			m_pSecondTicksNb[i]=a.m_pSecondTicksNb[i];
		}

		for (i=0;i<2;i++)
		{
			m_pStarts[i]=a.m_pStarts[i];
			m_pEnds[i]=a.m_pEnds[i];
			m_pSteps[i]=a.m_pSteps[i];
			m_pAutoSteps[i]=a.m_pAutoSteps[i];
			m_pGridVisible[i]=a.m_pGridVisible[i];
			m_pLogAxis[i]=a.m_pLogAxis[i];
		}

		m_rBorders=a.m_rBorders;
		m_pView=a.m_pView;
		m_bTimeLabel = a.m_bTimeLabel;
		m_tTimeLabelOrig = a.m_tTimeLabelOrig;
		m_tsDt = a.m_tsDt;
		m_sTimeLabel = a.m_sTimeLabel;
	}
	return *this;
}

	/// destructor
CPGLAxe2D::~CPGLAxe2D()
{
};

void CPGLAxe2D::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsVisible())
		return;

	// calling base class function
	CPGLObject::PlotGfx(gfx);

	// commentary
	gfx.AddComment("--- CPGLAxe2D ---");	

	// x grid
	if (m_pGridVisible[0])
		DrawGridGfx(0,gfx);
	// y grid
	if (m_pGridVisible[1])
		DrawGridGfx(1,gfx);

	// line of axe
	DrawLinesGfx(gfx);

	// draw numbering
	DrawNumbersGfx(gfx);
}

void CPGLAxe2D::SetLimits(int iaxe,double _start, double _end)
{
	// setting datas
	ASSERT(iaxe>=0);
	ASSERT(iaxe<2);
	if (_start==_end)
		_end++;

	if (!m_pLogAxis[iaxe])
	{
		m_pStarts[iaxe]=_start;
		m_pEnds[iaxe]=_end;
	}
	else
	{
		m_pStarts[iaxe]=log(_start);
		m_pEnds[iaxe]=log(_end);
	}
	// generating m_pStgfx...
	if (m_pAutoSteps[iaxe])
		GenerateSteps(iaxe);

	// repositioning label
	UpdateLabels();
}			

void CPGLAxe2D::GetLimits(int iaxe, double* pLimits)
{
	ASSERT(iaxe>=0);
	ASSERT(iaxe<2);
	pLimits[0]=m_pStarts[iaxe];
	pLimits[1]=m_pEnds[iaxe];
}


void CPGLAxe2D::GenerateSteps(int iaxe)
{
	ASSERT(iaxe >= 0 && iaxe <= 1);

	double dStart=m_pStarts[iaxe];
	double dEnd=m_pEnds[iaxe];
	
	if (m_pLogAxis[iaxe])
	{
		return ;
	}

	int numberOfTicks = 10; // TODO: Should be computed so that no overlap exists
	int bases [] = {1,5,2}; // Tick bases selection
	int currentBase;
	int n;
	double intervalSize, upperBound, lowerBound;
    int nIntervals,nMaxIntervals;

	// Vertical axis ticks
	if (fabs(dStart-dEnd) <= PGL_EPS) // Flat plo
	{
		dEnd=1;
		dStart=0;
	}
	
	// init step size
	m_pSteps[iaxe]=(dEnd-dStart)/2.0;

	int exponentYmax = (int)floor( log10(fabs(dEnd)) );
	double mantissaYmax = dEnd/pow(10,exponentYmax);        
	
	nMaxIntervals=0;
	for (int k = 1; k <= 4; ++k)
	{
        // Loop initialisation
        currentBase = bases[k];
        n = 4; // This value only allows results smaller than about 1000 = 10^n
		
		
        do // Tick vector length reduction 
        {
			--n;
			intervalSize = currentBase*pow(10, exponentYmax-n);
			upperBound =
				(int)ceil(mantissaYmax*pow(10,double(n))/currentBase)*intervalSize;
			nIntervals = (int)ceil((upperBound-dStart)/intervalSize);
			lowerBound = upperBound -nIntervals*intervalSize;
        }
        while ((nIntervals+1) > numberOfTicks);
        
        if (nIntervals > nMaxIntervals)
        {
			nMaxIntervals=nIntervals;
			m_pSteps[iaxe]=intervalSize;
			m_pStarts[iaxe]=lowerBound;
			m_pEnds[iaxe]=upperBound;
        }
	}
}

void CPGLAxe2D::DrawLinesGfx(gfxinterface::CGfxInterface& gfx)
{
	gfx.AddComment("--- CPGLAxe2D: Drawing lines and ticks");	
	// setting linewidth and line stipple
	gfx.SetDash("");
	gfx.SetLineWidth(1.8);

	gfx.DrawRect(m_pStarts[0], m_pStarts[1], m_pEnds[0], m_pEnds[1]);

	// bottom
	if (m_pNumbers[0].IsVisible())
	{
		gfx.DrawLine(m_pStarts[0],  m_pStarts[1] + m_pNumberPos[0]*(m_pEnds[1]-m_pStarts[1]),
			 m_pEnds[0], m_pStarts[1] + m_pNumberPos[0]*(m_pEnds[1]-m_pStarts[1]) );
	}

	//left
	if (m_pNumbers[1].IsVisible())
	{
		gfx.DrawLine( m_pStarts[0]+ m_pNumberPos[1]*(m_pEnds[0]-m_pStarts[0]) , m_pStarts[1],
				m_pStarts[0]+ m_pNumberPos[1]*(m_pEnds[0]-m_pStarts[0]) , m_pEnds[1]);

	}

	//right

	if (m_pNumbers[2].IsVisible())
	{
		gfx.DrawLine(m_pStarts[0]+ m_pNumberPos[2]*(m_pEnds[0]-m_pStarts[0]) , m_pStarts[1],
			m_pStarts[0]+ m_pNumberPos[2]*(m_pEnds[0]-m_pStarts[0]) , m_pEnds[1]);
	}

	//top
	if (m_pNumbers[3].IsVisible())
	{
		gfx.DrawLine(m_pStarts[0], m_pStarts[1] + m_pNumberPos[3]*(m_pEnds[1]-m_pStarts[1]),
				m_pEnds[0], m_pStarts[1] + m_pNumberPos[3]*(m_pEnds[1]-m_pStarts[1]));
	}

	// draw secondary ticks
	// bottom
	if (m_pSecondTicksNb[0]>0)
		gfx.DrawGridVer(m_pStarts[0],m_pEnds[0],m_pStarts[1],m_pStarts[1]+gfx.BBToWorldAbsy( m_pSecondTicksSize[0] ),m_pSteps[0]/(m_pSecondTicksNb[0]+1));
	//left
	if (m_pSecondTicksNb[1]>0)
		gfx.DrawGridHor(m_pStarts[0],m_pStarts[0]+gfx.BBToWorldAbsx( m_pSecondTicksSize[2] ),m_pStarts[1],m_pEnds[1],m_pSteps[1]/(m_pSecondTicksNb[1]+1));
	//right
	if (m_pSecondTicksNb[2]>0)
		gfx.DrawGridHor(m_pEnds[0],m_pEnds[0]-gfx.BBToWorldAbsx( m_pSecondTicksSize[3] ),m_pStarts[1],m_pEnds[1],m_pSteps[1]/(m_pSecondTicksNb[2]+1));
	// top
	if (m_pSecondTicksNb[3]>0)
		gfx.DrawGridVer(m_pStarts[0],m_pEnds[0],m_pEnds[1],m_pEnds[1]-gfx.BBToWorldAbsy( m_pSecondTicksSize[1] ),m_pSteps[0]/(m_pSecondTicksNb[3]+1));

	// draw main ticks
	gfx.DrawGridVer(m_pStarts[0],m_pEnds[0],m_pStarts[1],m_pStarts[1]+gfx.BBToWorldAbsy( m_pMainTicksSize[0] ),m_pSteps[0]);
	gfx.DrawGridVer(m_pStarts[0],m_pEnds[0],m_pEnds[1],m_pEnds[1]-gfx.BBToWorldAbsy( m_pMainTicksSize[1] ),m_pSteps[0]);
	gfx.DrawGridHor(m_pStarts[0],m_pStarts[0]+gfx.BBToWorldAbsx( m_pMainTicksSize[2] ),m_pStarts[1],m_pEnds[1],m_pSteps[1]);
	gfx.DrawGridHor(m_pEnds[0],m_pEnds[0]-gfx.BBToWorldAbsx( m_pMainTicksSize[3] ),m_pStarts[1],m_pEnds[1],m_pSteps[1]);

}

void CPGLAxe2D::DrawNumbersGfx(gfxinterface::CGfxInterface& gfx)
{
	// choosing and setting font
	CString str;
	CTime curtime;
	CTimeSpan dt;
	UINT nLabels,i;
	UINT nsec;
	UINT secnow; 
	double x,y;

	// sending grid number to gfx...	
	// bottom
	if (m_pNumbers[0].IsVisible())
	{
		// creating vectors
		nLabels=(UINT)(floor) ((m_pEnds[0]-m_pStarts[0])/m_pSteps[0])+1;
		std::vector<gfxinterface::SPointF> vPoints(nLabels);
		std::vector<std::string> vStrings(nLabels);

		// Creating strings...
		x=m_pStarts[0];
		y=m_pStarts[1] - gfx.BBToWorldAbsy( m_pNumbers[0].GetFont().GetHeight()+3 ) + m_pNumberPos[0]*(m_pEnds[1] - m_pStarts[1]);
		for (i=0;i<nLabels;i++)
		{
			// checking x
			if (fabs(x) < m_pSteps[0]*1e-3)
				x=0;

			// first point
			if (m_bTimeLabel)
			{
				nsec = m_tsDt.GetTotalSeconds();
				secnow = (int)floor(x*nsec);
				dt=CTimeSpan( secnow/(24*3600), 
					(secnow%(24*3600))/3600, 
					(secnow%3600)/60,
					secnow%60);
				curtime = m_tTimeLabelOrig+dt;
				str=curtime.Format(m_sTimeLabel);
			}
			else
			{
				str.Format("%g",x);
			}

			vPoints[i].x=x;
			vPoints[i].y=y;
			vStrings[i]=str;
			// updating x
			x+=m_pSteps[0];
		}
		// Drawing...
		// setting font
		m_pNumbers[0].GetFont().SetFontGfx(gfx);
		// setting color
		m_pNumbers[0].CPGLObject::PlotGfx(gfx);

		switch( m_pNumbers[0].GetAlign())
		{
		case PGL_TEXT_ALIGN_LEFT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentLeft, m_pNumbers[0].GetAngle());
			break;
		case PGL_TEXT_ALIGN_RIGHT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentRight, m_pNumbers[0].GetAngle());
			break;
		case PGL_TEXT_ALIGN_CENTER:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentCenter, m_pNumbers[0].GetAngle());
			break;
		}
	}

	// left
	if (m_pNumbers[1].IsVisible())
	{
		// creating vectors
		nLabels=(UINT)(floor) ((m_pEnds[1]-m_pStarts[1])/m_pSteps[1])+1;
		std::vector<gfxinterface::SPointF> vPoints(nLabels);
		std::vector<std::string> vStrings(nLabels);
		

		x=m_pStarts[0] - gfx.BBToWorldAbsx(3) + m_pNumberPos[1]*(m_pEnds[0] - m_pStarts[0]);
		y=m_pStarts[1];
		for (i=0;i<nLabels;i++)
		{
			// checking x
			if (fabs(y) < m_pSteps[1]*1e-3)
				y=0;
			// first point
			str.Format("%g",y);

			vPoints[i].x=x;
			vPoints[i].y=y;
			vStrings[i]=str;
			// updating y
			y+=m_pSteps[1];
		}

		// setting font
		m_pNumbers[1].GetFont().SetFontGfx(gfx);
		// setting color
		m_pNumbers[1].CPGLObject::PlotGfx(gfx);
		switch( m_pNumbers[1].GetAlign())
		{
		case PGL_TEXT_ALIGN_LEFT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentLeft, m_pNumbers[1].GetAngle());
			break;
		case PGL_TEXT_ALIGN_RIGHT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentRight, m_pNumbers[1].GetAngle());
			break;
		case PGL_TEXT_ALIGN_CENTER:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentCenter, m_pNumbers[1].GetAngle());
			break;
		}
	}

	// sending grid number to gfx...	
	// right
	if (m_pNumbers[2].IsVisible())
	{
		// creating vectors
		nLabels=(UINT)(floor) ((m_pEnds[1]-m_pStarts[1])/m_pSteps[1])+1;
		std::vector<gfxinterface::SPointF> vPoints(nLabels);
		std::vector<std::string> vStrings(nLabels);


		x=m_pStarts[0] + gfx.BBToWorldAbsx(3) + m_pNumberPos[2]*(m_pEnds[0] - m_pStarts[0]);
		y=m_pStarts[1];
		for (i=0;i<nLabels;i++)
		{
			// checking x
			if (fabs(y) < m_pSteps[1]*1e-3)
				y=0;
			// first point
			str.Format("%g",y);

			vPoints[i].x=x;
			vPoints[i].y=y;
			vStrings[i]=str;
			// updating y
			y+=m_pSteps[1];
		}
		// setting font
		m_pNumbers[2].GetFont().SetFontGfx(gfx);
		// setting color
		m_pNumbers[2].CPGLObject::PlotGfx(gfx);
		// Drawing...
		switch( m_pNumbers[2].GetAlign())
		{
		case PGL_TEXT_ALIGN_LEFT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentLeft, m_pNumbers[2].GetAngle());
			break;
		case PGL_TEXT_ALIGN_RIGHT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentRight, m_pNumbers[2].GetAngle());
			break;
		case PGL_TEXT_ALIGN_CENTER:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentCenter, m_pNumbers[2].GetAngle());
			break;
		}
	}

	// top
	if (m_pNumbers[3].IsVisible())
	{
		// creating vectors
		nLabels=(UINT)(floor) ((m_pEnds[1]-m_pStarts[1])/m_pSteps[1])+1;
		std::vector<gfxinterface::SPointF> vPoints(nLabels);
		std::vector<std::string> vStrings(nLabels);

		// setting font
		m_pNumbers[3].GetFont().SetFontGfx(gfx);
		// setting color
		m_pNumbers[3].CPGLObject::PlotGfx(gfx);

		x=m_pStarts[0];
		y=m_pStarts[1] + gfx.BBToWorldAbsy(3) + m_pNumberPos[3]*(m_pEnds[1] - m_pStarts[1]);
		for (i=0;i<nLabels;i++)
		{
			// checking x
			if (fabs(x) < m_pSteps[0]*1e-3)
				x=0;

			if (m_bTimeLabel)
			{
				nsec = m_tsDt.GetTotalSeconds();
				secnow = (int)floor(x*nsec);
				dt=CTimeSpan( secnow/(24*3600), 
					(secnow%(24*3600))/3600, 
					(secnow%3600)/60,
					secnow%60);
				curtime = m_tTimeLabelOrig+dt;
				str=curtime.Format(m_sTimeLabel);
			}
			else
			{
				str.Format("%g",x);
			}

			vPoints[i].x=x;
			vPoints[i].y=y;
			vStrings[i]=str;

			// updating x
			x+=m_pSteps[0];
		}

		// setting font
		m_pNumbers[3].GetFont().SetFontGfx(gfx);
		// setting color
		m_pNumbers[3].CPGLObject::PlotGfx(gfx);
		switch( m_pNumbers[3].GetAlign())
		{
		case PGL_TEXT_ALIGN_LEFT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentLeft, m_pNumbers[3].GetAngle());
			break;
		case PGL_TEXT_ALIGN_RIGHT:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentRight, m_pNumbers[3].GetAngle());
			break;
		case PGL_TEXT_ALIGN_CENTER:
			gfx.DrawTextStrip(vPoints, vStrings, gfxinterface::TextAlignmentCenter, m_pNumbers[3].GetAngle());
			break;
		}
	}


	// drawing labels...
	for (i=0;i<4;i++)
	{
		// writing down text...
		m_pLabels[i].PlotGfx(gfx);
	}
}

void CPGLAxe2D::DrawGridGfx(int iaxe, gfxinterface::CGfxInterface& gfx)
{
	gfx.SetDash("1 5");
	gfx.SetLineWidth(0);

	switch(iaxe)
	{
	case 0:
		gfx.DrawGridVer(m_pStarts[0],m_pEnds[0],m_pStarts[1],m_pEnds[1],m_pSteps[0]);
		break;
	case 1:
		gfx.DrawGridHor(m_pStarts[0],m_pEnds[0],m_pStarts[1],m_pEnds[1],m_pSteps[1]);
		break;
	}
}

void CPGLAxe2D::SetLabel(int iLabel, const CPGLText& newLabel)
{
	ASSERT_VALID(m_pView);
	ASSERT_VALID(&newLabel);
	ASSERT((iLabel>=0) && (iLabel<4));

	m_pLabels[iLabel]=newLabel;

	// placing label...
	UpdateLabels();
}

void CPGLAxe2D::SetLabel(int iLabel, const CString& newLabel)
{
	ASSERT_VALID(m_pView);
	ASSERT((iLabel>=0) && (iLabel<4));

	m_pLabels[iLabel].SetString(newLabel);

	// placing label...
	UpdateLabels();
}


void CPGLAxe2D::UpdateLabels()
{
	float pos[2];

	// bottom
	pos[0]=(float)((m_pStarts[0]+m_pEnds[0])/2.0);
	pos[1]=(float)(m_pStarts[1]-m_pView->PixelToWorld(1, m_pLabels[0].GetFont().GetHeight()+10));
	if (m_pNumbers[0].IsVisible())
		pos[1]-=m_pView->PixelToWorld(1, m_pNumbers[0].GetFont().GetHeight());
	m_pLabels[0].SetPosition(pos);

	// left
	pos[0]=(float)(m_pStarts[0]-m_pView->PixelToWorld(0, m_pLabels[1].GetFont().GetHeight()+5));
	if (m_pNumbers[1].IsVisible())
		pos[0]-=m_pView->PixelToWorld(0, m_pNumbers[1].GetFont().GetHeight()*4);
	pos[1]=(float)((m_pStarts[1]+m_pEnds[1])/2.0);
	m_pLabels[1].SetPosition(pos);

	// right
	pos[0]=(float)(m_pEnds[0]+m_pView->PixelToWorld(0, m_pLabels[2].GetFont().GetHeight()+5));
		if (m_pNumbers[2].IsVisible())
			pos[0]+=m_pView->PixelToWorld(0,m_pNumbers[2].GetFont().GetHeight()*4);
	pos[1]=(float)((m_pStarts[1]+m_pEnds[1])/2.0);
	m_pLabels[2].SetPosition(pos);

	// top
	pos[0]=(float)((m_pStarts[0]+m_pEnds[0])/2.0);
	pos[1]=(float)(m_pEnds[1]+m_pView->PixelToWorld(1, 20));
	if (m_pNumbers[3].IsVisible())
		pos[1]+=m_pView->PixelToWorld(1,m_pNumbers[3].GetFont().GetHeight()+5);
	m_pLabels[3].SetPosition(pos);
}

void CPGLAxe2D::Delete()
{
}

void CPGLAxe2D::Pan(double dx, double dy)
{
	m_pStarts[0]+=dx;
	m_pEnds[0]+=dx;
	m_pStarts[1]+=dy;
	m_pEnds[1]+=dy;

	UpdateLabels();
}

void CPGLAxe2D::ClipGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsVisible())
		return;

	gfx.AddComment("Axe2D: Clipping inside axis");
	gfx.PushClipWorld(m_pStarts[0],m_pStarts[1],m_pEnds[0],m_pEnds[1]);
}

void CPGLAxe2D::UnClipGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsVisible())
		return;

	gfx.AddComment("Axe2D: Unclipping");
	gfx.PopClipWorld();
}


void CPGLAxe2D::AddPropertyPage(CPropertySheet *pPropSheet)
{
	ASSERT_VALID(pPropSheet);

	// call own functions
	CPGLAxe2DPropPage* propPage=new CPGLAxe2DPropPage(this);
	pPropSheet->AddPage(propPage);
}

HTREEITEM CPGLAxe2D::AddPropTree(CTreeCtrl* pTree, HTREEITEM hParent)
{
	int i;
	ASSERT_VALID(pTree);
	CImageList* pImgList=pTree->GetImageList(TVSIL_NORMAL);
	ASSERT_VALID(pImgList);
	ASSERT(hParent);
	COLORREF crMask=0;
	// adding bitmap
	pImgList->Add(GetBitmap(),crMask);
	// adding to tree
	HTREEITEM htAxe;
	htAxe=pTree->InsertItem(TVIF_TEXT | TVIF_IMAGE | TVIF_PARAM  /* nMask*/, 
				GetName() /* lpszItem*/, 
				pImgList->GetImageCount()-1 /* nImage */, 
				0 /* nSelectedImage */, 
				0 /* nState */, 
				0 /* nStateMask */, 
				GetID() /*  lParam */, 
				hParent /* hParent */, 
				TVI_LAST /* hInsertAfter */);
	ASSERT(htAxe!=NULL);

	// adding title and labels
	for (i=0;i<4;i++)
	{
		m_pLabels[i].AddPropTree(pTree,htAxe);
	}

	// adding numbers
	for (i=0;i<4;i++)
	{
		m_pNumbers[i].AddPropTree(pTree,htAxe);
	}

	return htAxe;
}


CPGLObject* CPGLAxe2D::FindObject(UINT ID)
{
	// test if it is himself
	if (CheckID(ID))
		return this;

	// check if it is title
	for (int i=0;i<4;i++)
	{
		if (m_pLabels[i].CheckID(ID))
			return &m_pLabels[i];
	}

	// check if it is numbering
	for (i=0;i<4;i++)
	{
		if (m_pNumbers[i].CheckID(ID))
			return &m_pNumbers[i];
	}

	// none return NULL
	return NULL;
}

void CPGLAxe2D::SetTimeLabelFormat(const COleDateTime& _otOrig, const COleDateTimeSpan& _otsDt, LPCSTR _szTimeLabel)
{
	CTime _tOrig( _otOrig.GetYear(),
		_otOrig.GetMonth(),
		_otOrig.GetDay(),
		_otOrig.GetHour(),
		_otOrig.GetMinute(),
		_otOrig.GetSecond());

	CTimeSpan _tsDt(_otsDt.GetDays(),
		_otsDt.GetHours(),
		_otsDt.GetMinutes(),
		_otsDt.GetSeconds());

	SetTimeLabelFormat(_tOrig, _tsDt, _szTimeLabel);
}


void CPGLAxe2D::GrowExtent(double *pExt)
{
	ASSERT_VALID(m_pView);

	///////////////////////////////////////////////////////////////////////////////////////////////
	// getting screen size (in pixels)
	int iW=m_pView->GetWidth();
	int iH=m_pView->GetHeight();

	//////////////////////////////////////////////////////////////////////////////////////////////
	// Compute optimal borders...
	// left
	m_rBorders.left=10;
	if ( GetLeftLabel()->IsVisible() )
		m_rBorders.left+= GetLeftLabel()->GetFont().GetHeight() + 10; 
	if ( GetLeftNumber()->IsVisible() )
	{
		m_rBorders.left+= GetLeftNumber()->GetFont().GetHeight()*4;
	}

	// right
	m_rBorders.right=10;
	if ( GetRightLabel()->IsVisible() )
		m_rBorders.right+= GetRightLabel()->GetFont().GetHeight() + 10; 
	if ( GetRightNumber()->IsVisible())
		m_rBorders.right+= GetRightNumber()->GetFont().GetHeight()*4;

	// bottom
	m_rBorders.bottom=10;
	if ( GetBottomLabel()->IsVisible())
		m_rBorders.bottom+= GetBottomLabel()->GetFont().GetHeight() + 10; 
	if ( GetBottomNumber()->IsVisible() )
	{
		m_rBorders.bottom+= GetBottomNumber()->GetFont().GetHeight()+ 8;
	}

	// top
	m_rBorders.top=10;
	if ( GetTopLabel()->IsVisible())
		m_rBorders.top+= GetTopLabel()->GetFont().GetHeight() + 10; 
	if ( GetTopNumber()->IsVisible() )
		m_rBorders.top+= GetTopNumber()->GetFont().GetHeight()+ 8;

	/////////////////////////////////////////////////////////////////////////////////////////////
	// Horizontal scaling
	// we want to have iAxeLeft pixels on the left and iAxeRight pixels on the right...
	int iWPlot=__max(1,iW-m_rBorders.left-m_rBorders.right);	// pixels left to draw data
	double dWScale=(pExt[1]-pExt[0])/iWPlot;

	// updating extent
	pExt[0]-=m_rBorders.left*dWScale;
	pExt[1]+=m_rBorders.right*dWScale;

	/////////////////////////////////////////////////////////////////////////////////////////////
	// Vertical scaling
	int iHPlot=__max(1,iH-m_rBorders.bottom-m_rBorders.top);	// pixels left to draw data
	double dHScale=(pExt[3]-pExt[2])/iHPlot;

	// updating extent
	pExt[2]-=m_rBorders.bottom*dHScale;
	pExt[3]+=m_rBorders.top*dHScale;
}

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 GNU Lesser General Public License (LGPLv3)


Written By
Engineer
United States United States
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Comments and Discussions