// Copyright 2000
// College of Computer Science
// Northeastern University Boston MA 02115

// This software may be used for educational purposes as long as this copyright
// notice is retained at the top of all files

// Should this software be modified, the words "Modified from Original" must be
// included as a comment below this notice

// All publication rights are retained.  This software or its documentation may
// not be published in any media either in whole or in part.

///////////////////////////////////////////////////////////////////////////////

// Graphics.h

///////////////////////////////////////////////////////////////////////////////


#ifndef GRAPHICS_H_
#define GRAPHICS_H_

#include "SystemBase.h"
#include "RgbNames.h"


// utility functions

	// clear graphics window and reset graphics state
	// this function name is available for compatibility with earlier Core Tools

inline void ClearDrawing(int index = -1)
	{ ClearGraphicsWindow(index); }


	// get client rectangle information

inline void GetDrawingRect(RectData& R) {
	GraphicsWindow* gwp = GraphicsWindowPtr();
	R = gwp->GetClientRect();
}


inline void GetDrawingRectSize(short& x, short& y) {
	GraphicsWindow* gwp = GraphicsWindowPtr();
	gwp->GetClientSize(x, y);
}


// All drawing refers to the current default graphics window


// set and get for individual pixels

inline void SetPixelColor(short x, short y, byte r, byte g, byte b) {
	PixelWidget PW(x, y, r, g, b);
	PW.Draw();
}


inline void SetPixelColor(short x, short y, const RGBdata& color) {
	PixelWidget PW(x, y, color);
	PW.Draw();
}


inline void SetPixelColor(const PointData& p, byte r, byte g, byte b) {
	PixelWidget PW(p, r, g, b);
	PW.Draw();
}


inline void SetPixelColor(const PointData& p, const RGBdata& color) {
	PixelWidget PW(p, color);
	PW.Draw();
}


inline void SetPixelShade(short x, short y, byte shade) {
	PixelWidget PW(x, y, shade, shade, shade);
	PW.Draw();
}


inline void SetPixelShade(const PointData& p, byte shade) {
	PixelWidget PW(p, shade, shade, shade);
	PW.Draw();
}


inline void GetPixelColor(short x, short y, byte& r, byte& g, byte& b) {
	PixelWidget PW;
	PW.SetPixel(x, y).Peek().GetColor(r, g, b);
}


inline void GetPixelColor(short x, short y, short& r, short& g, short& b) {
	PixelWidget PW;
	PW.SetPixel(x, y).Peek().GetColor(r, g, b);
}


inline void GetPixelColor(short x, short y, RGBdata& color) {
	PixelWidget PW;
	PW.SetPixel(x, y).Peek().GetColor(color);
}


inline void GetPixelColor(const PointData& p, byte& r, byte& g, byte& b) {
	PixelWidget PW;
	PW.SetPixel(p).Peek().GetColor(r, g, b);
}


inline void GetPixelColor(const PointData& p, short& r, short& g, short& b) {
	PixelWidget PW;
	PW.SetPixel(p).Peek().GetColor(r, g, b);
}


inline void GetPixelColor(const PointData& p, RGBdata& color) {
	PixelWidget PW;
	PW.SetPixel(p).Peek().GetColor(color);
}


// move and line operations for pixel coordinates

inline void MoveTo(short x, short y) {
	MoveToWidget MT(x, y);
	MT.Draw();
}


inline void Move(short dx, short dy) {
	DeltaMoveWidget DM(dx, dy);
	DM.Draw();
}


inline void LineTo(short x, short y) {
	LineToWidget LT(x, y);
	LT.Draw();
}


inline void Line(short dx, short dy) {
	DeltaLineWidget DL(dx, dy);
	DL.Draw();
}


inline void DrawLine(short x1, short y1, short x2, short y2) {
	LineWidget LW(x1, y1, x2, y2);
	LW.Draw();
}


inline void MoveTo(const PointData& P) {
	MoveToWidget MT(P);
	MT.Draw();
}


inline void Move(const PointData& Delta) {
	DeltaMoveWidget DM(Delta);
	DM.Draw();
}


inline void LineTo(const PointData& P) {
	LineToWidget LT(P);
	LT.Draw();
}


inline void Line(const PointData& Delta) {
	DeltaLineWidget DL(Delta);
	DL.Draw();
}


inline void DrawLine(const PointData& P, const PointData& Q) {
	LineWidget LW(P, Q);
	LW.Draw();
}


// move and line operations for 2D data coordinates

inline void MoveTo2D(double x, double y) {
	MoveToWidget2D MT(x, y);
	MT.Draw();
}


inline void Move2D(double dx, double dy) {
	DeltaMoveWidget2D DM(dx, dy);
	DM.Draw();
}


inline void LineTo2D(double x, double y) {
	LineToWidget2D LT(x, y);
	LT.Draw();
}


inline void Line2D(double dx, double dy) {
	DeltaLineWidget2D DL(dx, dy);
	DL.Draw();
}


inline void DrawLine2D(double x1, double y1, double x2, double y2) {
	LineWidget2D LW(x1, y1, x2, y2);
	LW.Draw();
}


inline void MoveTo2D(const Point2D& P) {
	MoveToWidget2D MT(P);
	MT.Draw();
}


inline void Move2D(const Point2D& Delta) {
	DeltaMoveWidget2D DM(Delta);
	DM.Draw();
}


inline void LineTo2D(const Point2D& P) {
	LineToWidget2D LT(P);
	LT.Draw();
}


inline void Line2D(const Point2D& Delta) {
	DeltaLineWidget2D DL(Delta);
	DL.Draw();
}


inline void DrawLine2D(const Point2D& P, const Point2D& Q) {
	LineWidget2D LW(P, Q);
	LW.Draw();
}

// pen operations

inline void GetPenSpot(short& x, short& y) {
	GraphicsState GS = GetGraphicsState();
	GS.Location.Get(x, y);
}


inline void GetPenSpot(PointData& P)  {
	GraphicsState GS = GetGraphicsState();
	GS.Location.Get(P);
}


inline void SetPenSize(short xpen, short ypen) {
	PenSizeWidget PS;
	PS.Set(xpen, ypen).Draw();
}


inline void GetPenSize(short& xpen, short& ypen) {
	GraphicsState GS = GetGraphicsState();
	GS.PenSize.Get(xpen, ypen);
}


// rect operations

inline void FrameRect(short x1, short y1, short x2, short y2) {
	RectWidget RW;
	RW.Set(x1, y1, x2, y2).SetFrame().Draw();
}


inline void FillRect(short x1, short y1, short x2, short y2) {
	RectWidget RW;
	RW.Set(x1, y1, x2, y2).SetFill().Draw();
}


inline void FrameFillRect(short x1, short y1, short x2, short y2) {
	RectWidget RW;
	RW.Set(x1, y1, x2, y2).SetFrameFill().Draw();
}


inline void PaintRect(short x1, short y1, short x2, short y2)
	{ FrameFillRect(x1, y1, x2, y2); }


inline void EraseRect(short x1, short y1, short x2, short y2) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetWhite().Draw();

	RectWidget RW;
	RW.Set(x1, y1, x2, y2).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void InvertRect(short x1, short y1, short x2, short y2) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetInvert().Draw();

	RectWidget RW;
	RW.Set(x1, y1, x2, y2).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void FrameRect(const RectData& R) {
	RectWidget RW;
	RW.Set(R).SetFrame().Draw();
}


inline void FillRect(const RectData& R) {
	RectWidget RW;
	RW.Set(R).SetFill().Draw();
}


inline void FrameFillRect(const RectData& R) {
	RectWidget RW;
	RW.Set(R).SetFrameFill().Draw();
}


inline void PaintRect(const RectData& R)
	{ FrameFillRect(R); }


inline void EraseRect(const RectData& R)  {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetWhite().Draw();

	RectWidget RW;
	RW.Set(R).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void InvertRect(const RectData& R) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetInvert().Draw();

	RectWidget RW;
	RW.Set(R).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


// oval operations

inline void FrameOval(short x1, short y1, short x2, short y2) {
	OvalWidget OW;
	OW.Set(x1, y1, x2, y2).SetFrame().Draw();
}


inline void FillOval(short x1, short y1, short x2, short y2) {
	OvalWidget OW;
	OW.Set(x1, y1, x2, y2).SetFill().Draw();
}


inline void FrameFillOval(short x1, short y1, short x2, short y2) {
	OvalWidget OW;
	OW.Set(x1, y1, x2, y2).SetFrameFill().Draw();
}


inline void PaintOval(short x1, short y1, short x2, short y2)
	{ FrameFillOval(x1, y1, x2, y2); }


inline void EraseOval(short x1, short y1, short x2, short y2) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetWhite().Draw();

	OvalWidget OW;
	OW.Set(x1, y1, x2, y2).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void InvertOval(short x1, short y1, short x2, short y2) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetInvert().Draw();

	OvalWidget OW;
	OW.Set(x1, y1, x2, y2).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void FrameOval(const RectData& R) {
	OvalWidget OW;
	OW.Set(R).SetFrame().Draw();
}


inline void FillOval(const RectData& R) {
	OvalWidget OW;
	OW.Set(R).SetFill().Draw();
}


inline void FrameFillOval(const RectData& R) {
	OvalWidget OW;
	OW.Set(R).SetFrameFill().Draw();
}


inline void PaintOval(const RectData& R)
	{ FrameFillOval(R); }


inline void EraseOval(const RectData& R)  {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetWhite().Draw();

	OvalWidget OW;
	OW.Set(R).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


inline void InvertOval(const RectData& R) {
	GraphicsState GS = GraphicsWindowPtr()->GetState();

	PenModeWidget PM;
	PM.SetInvert().Draw();

	OvalWidget OW;
	OW.Set(R).SetFrameFill().Draw();

	PM.Set(GS.PenMode).Draw();
}


// circle operations

inline void FrameCircle(short x, short y, short radius) {
	FrameOval(x - radius, y - radius, x + radius, y + radius);
}


inline void FillCircle(short x, short y, short radius) {
	FillOval(x - radius, y - radius, x + radius, y + radius);
}


inline void FrameFillCircle(short x, short y, short radius) {
	FrameFillOval(x - radius, y - radius, x + radius, y + radius);
}


inline void PaintCircle(short x, short y, short radius) {
	FrameFillOval(x - radius, y - radius, x + radius, y + radius);
}


inline void EraseCircle(short x, short y, short radius) {
	EraseOval(x - radius, y - radius, x + radius, y + radius);
}


inline void InvertCircle(short x, short y, short radius) {
	InvertOval(x - radius, y - radius, x + radius, y + radius);
}


inline void FrameCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	FrameCircle(x, y, radius);
}


inline void FillCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	FillCircle(x, y, radius);
}


inline void FrameFillCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	FrameFillCircle(x, y, radius);
}


inline void PaintCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	PaintCircle(x, y, radius);
}


inline void EraseCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	EraseCircle(x, y, radius);
}


inline void InvertCircle(const PointData& center, short radius) {
	short x, y;
	center.Get(x, y);
	InvertCircle(x, y, radius);
}


// color settings

// PenColor:	Color of pen for line drawing and for frames of regions
// FillColor:	Color for region fill
// TextColor:	Color for interior of text characters in a graphics window
// BackColor:	Color for exterior of text characters if Opaque mode is set
//
// SetForeColor sets the Pen, Fill, and Text colors to the same Color


inline void SetPenColor(const RGBdata& Color) {
	PenColorWidget CW;
	CW.Set(Color).Draw();
}


inline void SetFillColor(const RGBdata& Color) {
	FillColorWidget CW;
	CW.Set(Color).Draw();
}


inline void SetTextColor(const RGBdata& Color) {
	TextColorWidget CW;
	CW.Set(Color).Draw();
}


inline void SetBackColor(const RGBdata& Color)  {
	BackgroundColorWidget CW;
	CW.Set(Color).Draw();
}


inline void SetForeColor(const RGBdata& Color) {
	SetPenColor(Color);
	SetFillColor(Color);
	SetTextColor(Color);
}


inline void GetPenColor(RGBdata& Color) {
	GraphicsState GS = GetGraphicsState();
	Color = GS.PenColor;
}


inline void GetFillColor(RGBdata& Color) {
	GraphicsState GS = GetGraphicsState();
	Color = GS.FillColor;
}


inline void GetTextColor(RGBdata& Color) {
	GraphicsState GS = GetGraphicsState();
	Color = GS.TextColor;
}


inline void GetBackColor(RGBdata& Color) {
	GraphicsState GS = GetGraphicsState();
	Color = GS.BackgroundColor;
}


inline void SetPenColor(byte r, byte g, byte b) {
	PenColorWidget CW;
	CW.Set(r, g, b).Draw();
}


inline void SetFillColor(byte r, byte g, byte b) {
	FillColorWidget CW;
	CW.Set(r, g, b).Draw();
}


inline void SetTextColor(byte r, byte g, byte b) {
	TextColorWidget CW;
	CW.Set(r, g, b).Draw();
}


inline void SetBackColor(byte r, byte g, byte b) {
	BackgroundColorWidget CW;
	CW.Set(r, g, b).Draw();
}


inline void SetForeColor(byte r, byte g, byte b) {
	SetPenColor(r, g, b);
	SetFillColor(r, g, b);
	SetTextColor(r, g, b);
}


inline void GetPenColor(byte& r, byte& g, byte& b) {
	GraphicsState GS = GetGraphicsState();
	GS.PenColor.Get(r, g, b);
}


inline void GetFillColor(byte& r, byte& g, byte& b) {
	GraphicsState GS = GetGraphicsState();
	GS.FillColor.Get(r, g, b);
}


inline void GetTextColor(byte& r, byte& g, byte& b) {
	GraphicsState GS = GetGraphicsState();
	GS.TextColor.Get(r, g, b);
}


inline void GetBackColor(byte& r, byte& g, byte& b) {
	GraphicsState GS = GetGraphicsState();
	GS.BackgroundColor.Get(r, g, b);
}


inline void GetPenColor(short& r, short& g, short& b) {
	GraphicsState GS = GetGraphicsState();
	GS.PenColor.Get(r, g, b);
}


inline void GetFillColor(short& r, short& g, short& b) {
	GraphicsState GS = GetGraphicsState();
	GS.FillColor.Get(r, g, b);
}


inline void GetTextColor(short& r, short& g, short& b) {
	GraphicsState GS = GetGraphicsState();
	GS.TextColor.Get(r, g, b);
}


inline void GetBackColor(short& r, short& g, short& b) {
	GraphicsState GS = GetGraphicsState();
	GS.BackgroundColor.Get(r, g, b);
}


// text

// show the text
//
//    in the current graphics window
//    at the current pen location
//    using the current text settings
//
// the current pen location is not changed

inline void ShowText(const string& text) {
	TextWidget TW;
	TW.Set(text).Draw();
}


// The aligncode is a horizontal align code, a vertical align code, or
// a combination of one of each combined with bitwise or: |
//
// The horizontal align codes are:
//
//    HAlign_Left
//    HAlign_Center
//    HAlign_Right
//
// The vertical align codes are:
//
//    VAlign_Top
//    VAlign_Baseline
//    VAlign_Bottom
//
// The default is left aligned and at the top of the character cell
//
// To change this default for example to centered and baseline, use:
//
//    SetTextAlignment(HAlign_Center | VAlign_Baseline);


inline void SetTextAlignment(int aligncode) {
	TextAlignWidget TA;
	TA.Set(aligncode).Draw();
}


// The background mode determines whether the spaces between characters show through
// (BM_Transparent) or are filled with the background color (BM_Opaque).
// The default mode is BM_Transparent.

inline void SetBackgroundMode(int mode) {
	BackgroundModeWidget BM;
	BM.Set(mode).Draw();
}


// marks: decorations that may be drawn at the current pen location
// all marks use the current pen color

enum Marks {
	// simple open marks
	DotMark,			// simple dot
	HBarMark,			// horizontal bar: can be used for vertical axis tick
	VBarMark,			// vertical bar: can be used for horizontal axis tick
	PlusMark,			// plus:  +
	CrossMark,			// cross: x
	StarMark,			// star:  *
	
	// simple closed marks
	SquareMark,			// open square
	FillSquareMark,		// filled square
	
	CircleMark,			// open circle
	FillCircleMark,		// filled circle
	
	WedgeUMark,			// triangle wedge up
	FillWedgeUMark,		// filled triangle wedge up
	
	WedgeDMark,			// triangle wedge down
	FillWedgeDMark,		// filled triangle wedge down
	
	WedgeLMark,			// triangle wedge left
	FillWedgeLMark,		// filled triangle wedge left
	
	WedgeRMark,			// triangle wedge right
	FillWedgeRMark,		// filled triangle wedge right

	DiamondMark,		// open diamond
	FillDiamondMark		// filled diamond
};


const short DefaultMarkSize = 3;


// Fundamental mark routine
//		Draws the mark in the desired size at the current graphics location
//		and maintains that location for subsequent graphics

EXPORT void Mark(short mark, short size = DefaultMarkSize);


// Pixel data plot and mark routines

// A PointDataArray Data is a structure that supports the [ ] operator.
// In addition, Data[i] must be of type PointData

template <class PointDataArray>
inline void PlotData(
	const PointDataArray& Data,		// data array
	int ValidSize					// amount of valid data in array
) {
	if (ValidSize > 0) {
		// move to initial data point
		MoveTo(Data[0]);
		
		// draw lines to successive data points
		// include Data[0] in case array has just one point

		for (int i = 0; i < ValidSize; i++)
			LineTo(Data[i]);
	}
}


template <class PointDataArray>
inline void MarkData(
	const PointDataArray& Data,		// data array
	int ValidSize,					// amount of valid data in array
	short mark,						// mark code
	short size = DefaultMarkSize	// size
) {
	for (int i = 0; i < ValidSize; i++) {
		MoveTo(Data[i]);
		Mark(mark, size);
	}
}


// 2D data plot and mark routines
//
// These 2D routines are much more extensive than the pixel routines
// since we expect these to be normal routines to plot graphs
// These 2D routines assume the current transform is set


// A Point2DArray Data is a structure that supports the [ ] operator.
// In addition, Data[i] must be of type Point2D

template <class Point2DArray>
inline void PlotData2D(
	const Point2DArray& Data,		// data array
	int ValidSize					// amount of valid data in array
) {
	if (ValidSize > 0) {
		// move to initial data point
		MoveTo2D(Data[0]);
		
		// draw lines to successive data points
		// include Data[0] in case array has just one point

		for (int i = 0; i < ValidSize; i++)
			LineTo2D(Data[i]);
	}
}


template <class Point2DArray>
inline void MarkData2D(
	const Point2DArray& Data,		// data array
	int ValidSize,					// amount of valid data in array
	short mark,						// mark code
	short size = DefaultMarkSize	// size
) {
	for (int i = 0; i < ValidSize; i++) {
		MoveTo2D(Data[i]);
		Mark(mark, size);
	}
}


// 2D data bounds routines that may be used to define the scaling transform

// In GrowBounds2D, assume that Bounds rectangle is initialized with earlier
// data and that the goal is to expand Bounds to include additional data

template <class Point2DArray>
inline void GrowBounds2D(
	Rect2D& Bounds,					// bounds rectangle to calculate
	const Point2DArray& Data,		// data array
	int ValidSize					// amount of valid data in array
) {
	for (int i = 0; i < ValidSize; i++)
		Bounds.Grow(Data[i]);
}


// In FindBounds2D, assume that Bounds rectangle is not initialized

template <class Point2DArray>
inline void FindBounds2D(
	Rect2D& Bounds,					// bounds rectangle to calculate
	const Point2DArray& Data,		// data array
	int ValidSize					// amount of valid data in array
) {
	// initialize Bounds
	Point2D P;						// P initialized to origin
	
	if (ValidSize > 0)				// if data exists, reset
		P = Data[0];				// P to first data point
	
	Bounds.Set(P, P);				// set Bounds to include P
	
	// now handle remaining points using GrowBounds2D
	GrowBounds2D(Bounds, Data, ValidSize);
}


// Routine to set the current scaling transform
// Bounds represents the data limits in 2D coordinates
// Limits represents the window plot area in pixel coordinates
// The bool value TrueShape determines whether to force transform to preserve shape
//
// For other ways to set the transform see Scale2D.h and GraphicsWidget.h


inline SetTransform2D(
	const Rect2D& Bounds,
	const RectData& Limits,
	bool TrueShape = false
) {
	LinearScale2D T;

	if (TrueShape)
		T.SetTrueShape(Bounds, Limits);
	else
		T.Set(Bounds, Limits);

	TransformWidget Widget(T);
	Widget.Draw();
}


// Other 2D plot routines
// These assume that the current scaling transform has been set

// Axes routines

EXPORT void PlotAxes2D(
	const Rect2D& Bounds			// data limits
);


// Note: In MarkAxes2D and PlotGrids2D, if xdelta <= 0 and/or ydelta <= 0
// then that direction is skipped 

EXPORT void MarkAxes2D(
	const Rect2D& Bounds,			// data limits
	double xdelta,					// spacing on x axis
	double ydelta,					// spacing on y axis
	int size = DefaultMarkSize		// mark size
);


EXPORT void PlotGrids2D(
	const Rect2D& Bounds,			// data limits
	double xdelta,					// spacing on x axis
	double ydelta					// spacing on y axis
);


// Routine to define good values for xdelta and ydelta
// for use in MarkAxes and PlotGrids
// Will seek to make each delta have the form f * 10^k
// where f = 1, 2, 5 and k is a short

EXPORT void GuessDelta2D(
	const Rect2D& Bounds,			// data bounds
	double& xdelta,					// spacing on x axis
	double& ydelta,					// spacing on y axis
	bool TrueShape = false			// if true then force xdelta == ydelta
);


// GuessDelta2D is based on the following 1-dimensional helper routine

EXPORT void GuessOne(double a1, double a2, double& delta);


// SetupPlot2D uses Bounds and Limits to:
//    1. Define and set the current scaling transform
//    2. Determine xdelta and ydelta in order to plot grids
//    3. Plot grids in green
//    4. Plot axes in black
// The TrueShape parameter is used in Steps 1 and 2 to make
// decisions.
//
// If you want to define an alternative routine to set up a
// plot, you may examine the code in Graphics.cpp for hints

EXPORT void SetupPlot2D(
	const Rect2D& Bounds,
	const RectData& Limits,
	bool TrueShape = false
);


// Routines to build data tables from function definitions

// All routines to build data tables need to know:
//		Data		A Polygon2D to store the Point2D data
//		ValidSize	number of data points to store
//		Lower		lower bound of parameter range in 2D coordinates
//		Upper		upper bound of parameter range in 2D coordinates

// Two cases:
//
//		Ordinary curve:			y = F(x)
//		Parametrized curve:		x = F(t)	y = G(t)
//
// These are distinguished by number of function paramters passed!


template <class DoubleFunction>
inline void BuildTable2D(
	Polygon2D& Data,				// Polygon2D to store the Point2D data
	int ValidSize,					// number of data points to store
	double Lower,					// lower bound of parameter range
	double Upper,					// upper bound of parameter range
	const DoubleFunction& F			// function for y = F(x)
) {
	// invalidate current polygon data
	Data.ValidateNone();

	// return if there is no array data to calculate
	if (ValidSize <= 0)
		return;
	
	// make the polygon have ValidSize points
	Data.ChangeMemory(ValidSize);

	// fill in the first position
	Data.Append(Lower, F(Lower));
	
	// return if there is only one data point
	if (ValidSize == 1)
		return;
	
	// n is the number of subdivisions between Lower and Upper
	int n = ValidSize - 1;
	
	// Delta is the size of these subdivisions
	double Delta = (Upper - Lower) / double(n);
	
	// x is a position variable
	double x = Lower + Delta;
	
	// fill in intermediate positions
	for (int i = 1; i < n; i++) {
		Data.Append(x, F(x));
		x += Delta;
	}
	
	// fill in the final position
	Data.Append(Upper, F(Upper));
}


template <class DoubleFunction>
inline void BuildTable2D(
	Polygon2D& Data,				// Polygon2D to store the Point2D data
	int ValidSize,					// number of data points to store
	double Lower,					// lower bound of parameter range
	double Upper,					// upper bound of parameter range
	const DoubleFunction& F,		// function for x = F(t)
	const DoubleFunction& G			// function for y = G(t)
) {
	// invalidate current polygon data
	Data.ValidateNone();

	// return if there is no array data to calculate
	if (ValidSize <= 0)
		return;
	
	// make the polygon have ValidSize points
	Data.ChangeMemory(ValidSize);

	// fill in the first position
	Data.Append(F(Lower), G(Lower));
	
	// return if there is only one data point
	if (ValidSize == 1)
		return;
	
	// n is the number of subdivisions between Lower and Upper
	int n = ValidSize - 1;
	
	// Delta is the size of these subdivisions
	double Delta = (Upper - Lower) / double(n);
	
	// t is a parameter variable
	double t = Lower + Delta;
	
	// fill in intermediate positions
	for (int i = 1; i < n; i++) {
		Data.Append(F(t), G(t));
		t += Delta;
	}
	
	// fill in the final position
	Data.Append(F(Upper), G(Upper));
}

#endif // GRAPHICS_H_
