(c) 2002 Visual Studio Magazine
Fawcette Technical Publications

Issue: August 2002
Section: Design Custom Charts
Author: Jonny Anderson

C#, GDI+	Wrap the Matrix Transformations Into the ChartBase Class
Listing 1	The ChartGrowthCurve and ChartBifurcation classes are derived from the ChartBase class. ChartBase provides each chart with an array of PointF objects and a protected matrix transformation procedure called ResizeMatrix.	

using System;
using System.Drawing;
using System.Drawing.Drawing2D;

public class ChartBase
{
private Rectangle m_TargetRectangle;
private Matrix m_MatrixCartesian=new Matrix();
private Matrix m_MatrixReflectX=new 
	Matrix(1F,0F,0F,-1F,0F,0F);
private PointF[] m_Points;

#region Declarations section and constructor
public ChartBase(int CountPoints)
{
	m_Points=new PointF[CountPoints];
}
#endregion 

#region Methods
protected void ResizeMatrix(float ScaleWidth, 
	float ScaleHeight, Rectangle TargetRectangle)
{
	m_TargetRectangle=TargetRectangle;
	m_MatrixCartesian.Reset();
	//Translate origin to bottom left
	m_MatrixCartesian.Translate(0, ScaleHeight);
	//Reflect about the X axis
	m_MatrixCartesian.Multiply(m_MatrixReflectX);
	//Scale to fit the Canvas
	m_MatrixCartesian.Scale(ScaleWidth, 
		ScaleHeight);
}
public void TransformPoints()
{
	m_MatrixCartesian.TransformPoints(m_Points);
}
#endregion 

#region Properties
public PointF[] Points
	{get{return m_Points;}}
public int Width
	{get{return m_TargetRectangle.Width;}}
public int Height
	{get{return m_TargetRectangle.Height;}}
#endregion 
} 

C#, GDI+	Call the DrawImage Method on the ChartGrowthCurve Class
Listing 2	The ChartGrowthCurve class draws a growth curve on a target surface whenever its DrawImage method is invoked. The public ResizeMatrix method ensures the final image is sized and oriented correctly. 

using System;
using System.Drawing;

public class ChartGrowthCurve: ChartBase
{
#region Declarations section and constructor
private const int m_PointCount=75;
private float m_GrowthRate;
private float m_GrowthRateIncrement;
public ChartGrowthCurve(float GrowthRate, float 
	GrowthRateIncrement): base(m_PointCount)
{
	m_GrowthRate=GrowthRate;
	m_GrowthRateIncrement=GrowthRateIncrement;
}
#endregion 

#region Methods
public void DrawImage(Graphics TargetSurface)
{
	//Fill the array with points
	float PopSize=0.05F;
	for(int i=0;i<m_PointCount;i++)
	{
		//The Logistic Equation
		PopSize=(m_GrowthRate*PopSize) * (1.0F-
			PopSize);
		//Save into the growth curve array
		base.Points[i].X = i;
		base.Points[i].Y = PopSize;
	}
	//Translate, reflect and scale the points
	base.TransformPoints();
	//Create a bitmap
	Bitmap Canvas = new Bitmap(base.Width, 
		base.Height);
	//Create an offscreen surface
	Graphics SurfaceOffScreen = 
		Graphics.FromImage(Canvas);
	//Flood the surface with a back color
	SurfaceOffScreen.Clear(Color.Black);
	//Draw growth curve
	SurfaceOffScreen.DrawLines(Pens.LightGreen, 
		base.Points);
	//Draw growth rate counter text
	Font TextFont=new Font("verdana", 8);
	SurfaceOffScreen.DrawString("Growth Rate = " + 
		string.Format("{0:F6}", m_GrowthRate) , 
		TextFont, Brushes.White, new Point(10,10));
	//Shift completed bitmap to display surface 
	TargetSurface.DrawImage(Canvas, 0, 0);
	//Dispose of the GDI+ objects
	Canvas.Dispose();
	SurfaceOffScreen.Dispose();
	TextFont.Dispose();
}
	
public void IncrementGrowthRate()
{
	//increment rate by the specified amount
	m_GrowthRate+=m_GrowthRateIncrement;
	//Reset rate if it exceeds the maximum
	if (m_GrowthRate>=4F)
		m_GrowthRate=0.95F;
}

public void ResizeMatrix(Rectangle 
	TargetRectangle)
{
	float ScaleWidth=TargetRectangle.Width/m_PointCount;
	float ScaleHeight=TargetRectangle.Height;
	base.ResizeMatrix(ScaleWidth, ScaleHeight, 
		TargetRectangle);
}
#endregion 

#region Properties
public float GrowthRate
	{set{m_GrowthRate=value;}}
public float GrowthRateIncrement
	{set{m_GrowthRateIncrement=value;}}
#endregion 

}
