using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Text;
using System.Drawing;
using VG;
namespace XYPlots
{
	/// 
	/// Summary description for XYPlot.
	/// 
	public class XYPlot
	{
		static string _MyFontFamily = "Letter Gothic Tall";
		public static string MyFontFamily
		{
			get { return _MyFontFamily; }
			set { _MyFontFamily = value; }
		}
		private static Color _BlackColor = Color.Red;
		public static Color BlackColor
		{
			get { return VG.VG.BlackColor; }
			set { VG.VG.BlackColor = value; }
		}
		public float Width
		{
			get { return 72 * (LeftMargin + RightMargin) + printerunits[0] * .05F; }
		}
		public float Height
		{
			get { return 72 * (TopMargin + BottomMargin) + printerunits[1] * .05F; }
		}
		public float PlotWidth
		{
			get { return (float) userunits[X]; }
		}
		public float PlotHeight
		{
			get { return (float) userunits[Y]; }
		}
		private double ConvertToTwips = 4.8;
		static int MAX_XY_STACK = 8;		/* this is max of CIE cursor/font stack */
		static int DPI = 1440; //300;
		static int CWIDEDOTS = 120; //25;
		static int CHIGHDOTS = 240; //50;
		static int STRAIGHT = 0;
		static int CURVE = 1;
		static int X = 0;
		static int Y = 1;
		static int Dimensions = 2;
		static int USER = 0;
		static int PLOT = 1;
		static double PI = 3.141592653;
		static double Zero = 0.0;
		static double ANGLELIMIT = 1.0;
		// !!!! NEED REAL SUPERSCRIPT ON/OFF TOKENS
		static string[] SuperScript = { "##", "##" };
		static string[] SubScript = { "~~", "~~" };
		FileStream XYPlotFile;
		private int[] SavedX;
		private int[] SavedY;
		private int stack;
		/// 
		/// Set All Margins at once
		/// 
		/// Left Margin in Inches.
		/// Top Margin in Inches.
		/// Right Margin in Inches.
		/// Bottom Margin in Inches.
		public void SetMargins(float leftMargin, float topMargin, float rightMargin, float bottomMargin)
		{
			_LeftMargin = leftMargin * 1440;
			_TopMargin = topMargin * 1440;
			_RightMargin = rightMargin * 1440;
			_BottomMargin = bottomMargin * 1440;
		}
		private float _LeftMargin = 1440;
		/// 
		/// Left Margin in Inches.
		/// 
		public float LeftMargin
		{
			get { return _LeftMargin / 1440; }
			set { _LeftMargin = value * 1440; }
		}
		private float _RightMargin = 720;
		/// 
		/// Right Margin in Inches.
		/// 
		public float RightMargin
		{
			get { return _RightMargin / 1440; }
			set { _RightMargin = value * 1440; }
		}
		private float _TopMargin = 1440;
		/// 
		/// Top Margin in Inches.
		/// 
		public float TopMargin
		{
			get { return _TopMargin / 1440; }
			set { _TopMargin = value * 1440; }
		}
		private float _BottomMargin = 1440;
		/// 
		/// Bottom Margin in Inches.
		/// 
		public float BottomMargin
		{
			get { return _BottomMargin / 1440; }
			set { _BottomMargin = value * 1440; }
		}
		public int LinesUsed;
		private string valuestring;
		private string powerstring;
		private int GridOnFlag, Visible, ShadowFlag;
		private char[] type = new char[Dimensions];
		private double FontPitch = 10.0;
		private double[] minimum = new double[Dimensions];
		private double[] maximum = new double[Dimensions];
		private double[] userunits = new double[Dimensions];
		private int[] printerunits = new int[Dimensions];
		private int[] Position = new int[Dimensions];
		private double[,] Scale = new double[2, Dimensions];
		private double[,] Offset = new double[2, Dimensions];
		private double[] delta = new double[Dimensions];
		private int[] cycles = new int[Dimensions];
		private int[] onecycle = new int[Dimensions];
		private int[] minor = new int[Dimensions];
		private int[] spcminor = new int[Dimensions];
		private string[] AxisTitles = new string[Dimensions];
		private int[] AxisLabel = new int[Dimensions];// 1 for YES(default), 0 for NO
		private int CurPenWidth, DrawDirection, LineFlag, LineDiv;
		private int EndFlag = 0;
		public string Buff; 
		private int BuffPtr = 0;
		private bool[,] doLOG10 = { { false, false }, { false, false } };
		public struct point
		{
			public int[] xyValue; //new int[Dimensions];
		}
		public struct XyBox
		{
			public int Shadow;
			public string BoxText;
			public point BoxMinimum;
			public point BoxMaximum;
		};
		public class XyBoxes : List
		{
		}
		private XyBoxes AllBoxes = new XyBoxes();
		private XyBoxes ActiveBoxes = new XyBoxes();
		public class DataPoint
		{
			private point m_APoint;
			public point APoint
			{
				get { return m_APoint; }
				set { m_APoint = value; }
			}
			private double m_slope;		// slope of tangent to circle at this point
			public double slope
			{
				get { return m_slope; }
				set { m_slope = value; }
			}
		};
		public class DataPoints : List
		{
		}
		public struct PlotLine
		{
			public int PlotType;
			public int PlotDivisions;
			public DataPoints PlotDataPoints;
		};
		private List AllPlots = new List();
		private int YLandScape = 0;				/* y axis title is horizontal */
		private point YTitleOrigin;		/* y axis title origin if vertical */
		private double FixAngle(double a1)
		{
			double retval;
			int sign;
			if (a1 < 0)
			{
				a1 = -a1;
				sign = -1;
			}
			else
				sign = 1;
			retval = sign * Math.Floor(0.5 + a1);
			return retval;
		}
		private double dsquare(int val)
		{
			double result;
			result = (double)(val * val);
			return result;
		}
		private double FMin(double f1, double f2)
		{
			return ((f1 >= f2) ? f2 : f1);
		}
		private double FMax(double f1, double f2)
		{
			return ((f1 >= f2) ? f1 : f2);
		}
		private double atan_deg(double x)
		{
			return (Math.Atan(x) * 180.0 / PI);
		}
		private double cos_deg(double x)
		{
			return (Math.Cos(x * PI / 180.0));
		}
		private double tan_deg(double x)
		{
			return (Math.Tan(x * PI / 180.0));
		}
		private double sin_deg(double x)
		{
			return (Math.Sin(x * PI / 180.0));
		}
		/// 
		/// the following function is pointed to by a function pointer
		/// and is provided for compatibility with log10() function usage
		/// 
		/// 
		/// 
		private double linear(double x)
		{
			return x;
		}
		private bool isWhiteSpace(char cptr)
		{
			bool rtnval = false;
			if (!cptr.Equals(null))
			{
				int idx = "\t\n\r\f ".IndexOf(cptr);
				rtnval = (idx > -1);
			}
			return rtnval;
		}
		private string NextPiece()
		{
			string rtnval;
			int ptr;
			while (BuffPtr >= 0 && Buff[BuffPtr] != 0 && isWhiteSpace(Buff[BuffPtr])) BuffPtr++;
			ptr = BuffPtr;
			while (BuffPtr >= 0 && Buff[BuffPtr] != 0 && !isWhiteSpace(Buff[BuffPtr])) BuffPtr++;
			if (EndOfCommand()) EndFlag = 1;
			rtnval = Buff.Substring(ptr, BuffPtr - ptr);
			BuffPtr++;
			return rtnval;
		}
		private bool EndOfCommand()
		{
			int tmpPtr = BuffPtr;
			while (!Buff[tmpPtr].Equals('\0') && Buff[tmpPtr].Equals(' ')) tmpPtr++;
			if (!Buff[tmpPtr].Equals('\0') && !isWhiteSpace(Buff[tmpPtr]))
				return false;
			else if (Buff[tmpPtr] == '\r' || Buff[tmpPtr] == '\n' || Buff[tmpPtr] == '\0')
				return true;
			else
				return false;
		}
		private bool LoadBuff(string FileName)
		{
			if (!File.Exists(FileName))
			{
				// File does not exist, show error message
				MessageBox.Show(String.Format("X/Y Plot File {0} Does Not Exist", FileName), "Error Opening X/Y Plot File");
				return false;
			}
			// Open the X/Y Plot file
			try
			{
				XYPlotFile = File.OpenRead(FileName);
			}
			catch (Exception e)
			{
				// cannot open the x/y plot file
				MessageBox.Show(e.Message, String.Format("Error Opening {0} for Reading", FileName));
				return false;
			}
			int blen = (int)XYPlotFile.Length;
			int bread = 0;
			// initialize a byte array to read into
			byte[] bbuf = new byte[blen + 1];
			for (int i = 0; i < blen; i++) bbuf[i] = 0;
			// Read the contents of the x/y plot file into the byte array
			try
			{
				bread = XYPlotFile.Read(bbuf, 0, blen);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, String.Format("Error Reading {0}", FileName));
				return false;
			}
			try
			{
				XYPlotFile.Close();
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, String.Format("Error Closing {0}", FileName));
				return false;
			}
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < bbuf.Length; i++)
			{
				if (bbuf[i] == '\r' && bbuf[i - 1] == ' ')
					sb.Remove(i - 1, 1);
				sb.Append(Convert.ToChar(bbuf[i]));
			}
			Buff = sb.ToString();
			BuffPtr = 0;
			return true;
		}
		private void LoadBuffFromString(string str)
		{
			Buff = str;
		}
		private int getint()
		{
			int retval;
			retval = Convert.ToInt32(NextPiece(), 10);
			return retval;
		}
		private double getdouble()
		{
			double retval;
			retval = Convert.ToDouble(NextPiece());
			return retval;
		}
		private char NextChar()
		{
			return Buff[BuffPtr++];
		}
		private char getchr()
		{
			string nxtstring = NextPiece();
			if (nxtstring.Equals(""))
				return '\0';
			else
				return nxtstring[0];
		}
		private point GetPair(int flag)
		{
			point retval = new point();
			retval.xyValue = new int[Dimensions];
			double x, y;
			char[] sepchar = { ',' };
			string[] xystr = NextPiece().Split(sepchar);
			x = double.Parse(xystr[0]);
			y = double.Parse(xystr[1]);
			if (doLOG10[flag, X])
				retval.xyValue[X] = (int)(Offset[flag, X] + Scale[flag, X] * Math.Log10(x) + .5);
			else
				retval.xyValue[X] = (int)(Offset[flag, X] + Scale[flag, X] * x + .5);
			if (doLOG10[flag, Y])
				retval.xyValue[Y] = (int)(printerunits[Y] - (Offset[flag, Y] + Scale[flag, Y] * Math.Log10(y) + .5));
			else
				retval.xyValue[Y] = (int)(printerunits[Y] - (Offset[flag, Y] + Scale[flag, Y] * y + .5));
			return (retval);
		}
		private string GetString()
		{
			char retval;
			StringBuilder Lbuff = new StringBuilder();
			retval = NextChar();
			if (retval != '"')
				MessageBox.Show("Double Quote not found", "String value syntax problem");
			retval = NextChar();
			while (retval != 0 && retval != '\n')
			{
				switch (retval)
				{
					case '"':
						retval = NextChar();
						if (retval == ' ')
						{
							Lbuff.Append('\n');
							retval = NextChar();
							retval = NextChar();
						}
						break;
					case '\r':
						retval = NextChar();
						break;
					case '\\':
						retval = NextChar();		/*always take next*/
						Lbuff.Append(retval);
						retval = NextChar();
						break;
					default:
						Lbuff.Append(retval);
						retval = NextChar();
						break;
				}
			}
			return Lbuff.ToString();
		}
		private void FixBuffIfNeeded()
		{
			int ptr = 0;
			if (Buff.StartsWith("<\r\n<", ptr)) > 0)
			{
				// blank out ">"
				Buff = Buff.Remove(ptr, 1);
				Buff = Buff.Insert(ptr, " ");
				// remove "<"
				Buff = Buff.Remove(ptr + 3, 1);
				// if command on next line is blank,
				// combine with the previous line
				if (Buff[ptr + 3] == ' ')
					Buff = Buff.Remove(ptr, 3);
			}
			ptr = Buff.Length - 5; // back up past >>\r\n
			if (Buff.IndexOf(">>\r\n", ptr) > 0)
			{
				Buff = Buff.Remove(ptr, 2);
				Buff = Buff.Insert(ptr, "  ");
			}
			else if (Buff.EndsWith(">>")) // doesn't end with return chars...
				Buff = Buff.Substring(0, Buff.Length - 2) + "  \r\n\0x00"; // needs to end with null
		}
		private void CloseGraph()
		{
			/* Send back number of lines used assuming 6 lines per inch */
			if (LinesUsed == 0)
				LinesUsed = (int)(1.5 * CHIGHDOTS);		/* adjust for x axis tics */
			LinesUsed += printerunits[Y];
			LinesUsed = LinesUsed / CHIGHDOTS;
		}
		private void GetScaleInfo()
		{
			GridOnFlag = getint();
			userunits[X] = getdouble();
			userunits[Y] = getdouble();
			printerunits[X] = (int)(DPI * userunits[X]);
			printerunits[Y] = (int)(DPI * userunits[Y]);
			minimum[X] = getdouble();
			maximum[X] = getdouble();
			minimum[Y] = getdouble();
			maximum[Y] = getdouble();
			type[X] = getchr();
			type[Y] = getchr();
			if (EndFlag == 0) delta[X] = getdouble();
			if (EndFlag == 0) delta[Y] = getdouble();
			if (EndFlag == 0) spcminor[X] = getint();
			if (EndFlag == 0) spcminor[Y] = getint();
			Offset[USER, X] = 0;
			Offset[USER, Y] = 0;
			Scale[USER, X] = DPI;
			Scale[USER, Y] = DPI;
			/*
				  in the following if blocks, the array variable f is a function
				  pointer which is set to either the function linear or log10.
			*/
			/* X-axis function generation and scaling */
			if (type[X] != 'L')
			{
				// Linear
				doLOG10[PLOT, X] = false;
				LinearScale(X);
				Scale[PLOT, X] = printerunits[X] / (maximum[X] - minimum[X]);
				Offset[PLOT, X] = -Scale[PLOT, X] * minimum[X];
			}
			else
			{
				// Log10
				doLOG10[PLOT, X] = true;
				LogScale(X);
				Scale[PLOT, X] = printerunits[X] /
					(Math.Log10(maximum[X]) - Math.Log10(minimum[X]));
				Offset[PLOT, X] = -Scale[PLOT, X] * Math.Log10(minimum[X]);
			}
			/* Y-axis function generation and scaling */
			if (type[Y] != 'L')
			{
				// Linear
				doLOG10[PLOT, Y] = false;
				LinearScale(Y);
				Scale[PLOT, Y] = printerunits[Y] / (maximum[Y] - minimum[Y]);
				Offset[PLOT, Y] = -Scale[PLOT, Y] * minimum[Y];
			}
			else
			{
				// Log10
				doLOG10[PLOT, Y] = true;
				LogScale(Y);
				Scale[PLOT, Y] = printerunits[Y] /
					(Math.Log10(maximum[Y]) - Math.Log10(minimum[Y]));
				Offset[PLOT, Y] = -Scale[PLOT, Y] * Math.Log10(minimum[Y]);
			}
		}
		private void GetAxisTitle(int flag)
		{
			AxisTitles[flag] = GetString();
		}
		private string stripSuperSub(string instr)
		{
			StringBuilder sb = new StringBuilder(instr);
			sb.Replace(SuperScript[0], "");
			sb.Replace(SuperScript[1], "");
			sb.Replace(SubScript[0], "");
			sb.Replace(SubScript[1], "");
			return sb.ToString();
		}
		private point DetermineBoxMax(XyBox boxptr)
		{
			point newval = new point();
			newval.xyValue = new int[Dimensions];
			string tmp;
			int pos = 0, prevPos = 0;
			int nl = 0, xmax = 0, ilen = 0;
			tmp = stripSuperSub(boxptr.BoxText);
			tmp = tmp.Replace("\r\n", "\n");
			while ((pos = tmp.IndexOf('\n', pos)) > -1)
			{
				ilen = pos - prevPos;
				xmax = Math.Max(xmax, ilen);
				// OLD:  prevPos = pos;
				prevPos = pos + 1;
				pos++;
				nl++;
			}
			// OLD: ilen = tmp.Substring(prevPos + 1).Length;
			ilen = tmp.Length - prevPos; // tmp.Substring(prevPos).Length;
			xmax = Math.Max(xmax, ilen) + 2;
			newval.xyValue[X] = boxptr.BoxMinimum.xyValue[X] + (xmax * CWIDEDOTS);
			// OLD: newval.xyValue[Y] = boxptr.BoxMinimum.xyValue[Y] - (nl * CHIGHDOTS) - (int)(30 * ConvertToTwips);
			newval.xyValue[Y] = boxptr.BoxMinimum.xyValue[Y] - (nl * CHIGHDOTS) - (int)(19 * ConvertToTwips);
			return newval;
		}
		private void GetBoxParameters()
		{
			XyBox NewBox = new XyBox();
			point Origin;
			Origin = GetPair(USER);
			string str = GetString();
			NewBox.BoxText = str;
			NewBox.BoxMinimum = Origin;
			NewBox.Shadow = ShadowFlag;
			ShadowFlag = 0;
			NewBox.BoxMaximum = DetermineBoxMax(NewBox);
			point minPoint = new point();
			minPoint.xyValue = new int[Dimensions];
			point maxPoint = new point();
			maxPoint.xyValue = new int[Dimensions];
			minPoint.xyValue[X] = Origin.xyValue[X];
			minPoint.xyValue[Y] = NewBox.BoxMaximum.xyValue[Y];
			maxPoint.xyValue[X] = NewBox.BoxMaximum.xyValue[X];
			maxPoint.xyValue[Y] = Origin.xyValue[Y];
			NewBox.BoxMinimum = minPoint;
			NewBox.BoxMaximum = maxPoint;
			AllBoxes.Add(NewBox);
		}
		private void GetLineParameters()
		{
			DataPoints dataPoints = new DataPoints();
			int pointcount;
			pointcount = EndFlag = 0;
			while (EndFlag == 0)
			{
				DataPoint pl = new DataPoint();
				pl.APoint = new point();
				pl.APoint = GetPair(PLOT);
				pointcount++;
				dataPoints.Add(pl);
			}
			if (LineFlag == CURVE && pointcount > 2)
				CalculateSlopes(dataPoints);
			else
				LineFlag = STRAIGHT;
#if DEBUG
			ShowPoints(dataPoints);
#endif
			AddAPlot(dataPoints);
		}
		private double FindSlope(point first, point last)
		{
			double dx, dy, result;
			dx = last.xyValue[X] - first.xyValue[X];
			dy = (last.xyValue[Y] - first.xyValue[Y]);
			if (dx == 0.0)
			{
				if (dy > 0)
					result = Convert.ToDouble("1.E99");
				else
					result = Convert.ToDouble("-1.E99");
			}
			else
				result = dy / dx;
			return result;
		}
		private double FindEndSlope(point first, point last, double s1)
		{
			double result;
			result = 2.0 * FindSlope(first, last) - s1;
			if (result * s1 < 0.0)
				result = 0.0;
			return result;
		}
		private void CalculateSlopes(DataPoints dataPoints)
		{
			DataPoint thispt, prevpt, firstpt;
			long yval, y1, y3;
			int PlAryIdx = 0;
			int PlAryNumItems = dataPoints.Count;
			prevpt = dataPoints[PlAryIdx];
			PlAryIdx++;
			thispt = dataPoints[PlAryIdx];
			while (PlAryIdx + 1 < dataPoints.Count)
			{
				thispt.slope = Zero;
				yval = thispt.APoint.xyValue[Y];
				y1 = prevpt.APoint.xyValue[Y];
				y3 = dataPoints[PlAryIdx + 1].APoint.xyValue[Y];
				if ((yval - y1) * (y3 - yval) > 0)
					thispt.slope = FindSlope(prevpt.APoint, dataPoints[PlAryIdx + 1].APoint);
				prevpt = thispt;
				PlAryIdx++;
				thispt = dataPoints[PlAryIdx];
			}
			firstpt = dataPoints[0];
			firstpt.slope = FindEndSlope(firstpt.APoint, dataPoints[1].APoint, dataPoints[1].slope);
			thispt.slope = FindEndSlope(thispt.APoint, prevpt.APoint, prevpt.slope);
		}
		private void AddAPlot(DataPoints ptr)
		{
			PlotLine NewLine = new PlotLine();
			NewLine.PlotType = LineFlag;
			NewLine.PlotDivisions = LineDiv;
			NewLine.PlotDataPoints = ptr;
			AllPlots.Insert(0, NewLine);
		}
		private void GenerateGrid(VG.Page pg, IVGOutput vgOutput)
		{
			ActiveBoxes.Clear();
			Position[X] = 0;
			Position[Y] = 0; // printerunits[Y];
			SavePosition();
			if (GridOnFlag == 0) Visible = 0;
			//if (vgOutput is VGOut_Graphics)
			//{
				DoGrid(X, pg, vgOutput, 1);
				DoGrid(Y, pg, vgOutput, 1);
				DoGrid(X, pg, vgOutput, 2);
				DoGrid(Y, pg, vgOutput, 2);
				DoGrid(X, pg, vgOutput, 3);
				DoGrid(Y, pg, vgOutput, 3);
			//}
			//else if (vgOutput is VGOut_C1PDF)
			//{
			//  DrawDirection = Y;
			//  DoGrid(X, pg, vgOutput);
			//  DrawDirection = X;
			//  DoGrid(Y, pg, vgOutput);
			//}
			Visible = 1;
			RecallPosition();
			// Draw outline last to overlap any grid lines.
			SetPenDiameter(5);
			BoxRelative(printerunits[X], printerunits[Y], CurPenWidth, pg, vgOutput);
		}
		private void DoGrid(int flag, VG.Page pg, IVGOutput vgOutput)
		{
			if (type[flag] != 'L')
				DoLinearGrid(flag, pg, vgOutput);
			else
				DoLogGrid(flag, pg, vgOutput);
		}
		private void DoLinearGrid(int flag, VG.Page pg, IVGOutput vgOutput)
		{
			int i, n, ns, subminor;
			int ptval;
			n = (int)(0.5 + (maximum[flag] - minimum[flag]) / delta[flag]);
			ns = n * minor[flag];
			if ((minor[flag] % 2) != 0)
				subminor = minor[flag];
			else
				subminor = minor[flag] / 2;
			for (i = 0; i <= ns; i++)
			{
				SetPenDiameter((((i % minor[flag]) != 0) ? (((i % subminor) != 0) ? 1 : 2) : 3));
				ptval = ((int)printerunits[flag]) * i / ns;
				if (flag == Y)
					ptval = printerunits[Y] - ptval;
				MoveAbsolute(((flag == X) ? ptval : 0), ((flag == Y) ? ptval : 0));
				if (i % minor[flag] == 0)
					WriteValue(flag, minimum[flag] + i * delta[flag] / minor[flag], false, pg, vgOutput);
				DrawGridLine((flag == 0) ? 1 : 0, ptval, pg, vgOutput);
			}
		}
		private void DoGrid(int flag, VG.Page pg, IVGOutput vgOutput, int gridPen)
		{
			DrawDirection = 1 - flag;
			if (type[flag] != 'L')
				DoLinearGrid(flag, pg, vgOutput, gridPen);
			else
				if (gridPen == 3) DoLogGrid(flag, pg, vgOutput);
		}
		private void DoLinearGrid(int flag, VG.Page pg, IVGOutput vgOutput, int gridPen)
		{
			int i, n, ns, subminor;
			int ptval;
			n = (int)(0.5 + (maximum[flag] - minimum[flag]) / delta[flag]);
			ns = n * minor[flag];
			if ((minor[flag] % 2) != 0)
				subminor = minor[flag];
			else
				subminor = minor[flag] / 2;
			for (i = 0; i <= ns; i++)
			{
				int pd = (((i % minor[flag]) != 0) ? (((i % subminor) != 0) ? 1 : 2) : 3);
				if (pd == gridPen)
				{
					SetPenDiameter(pd);
					ptval = ((int)printerunits[flag]) * i / ns;
					if (flag == Y)
						ptval = printerunits[Y] - ptval;
					MoveAbsolute(((flag == X) ? ptval : 0), ((flag == Y) ? ptval : 0));
					if (i % minor[flag] == 0)
						WriteValue(flag, minimum[flag] + i * delta[flag] / minor[flag], false, pg, vgOutput);
					DrawGridLine((flag == 0) ? 1 : 0, ptval, pg, vgOutput);
				}
			}
		}
		private void DoLogGrid(int flag, VG.Page pg, IVGOutput vgOutput)
		{
			double showval;
			int gs, ge, gd;
			int i, ptval;
			showval = minimum[flag];
			for (i = 0; i < cycles[flag]; i++)
			{
				gs = gd = 100;
				ge = 1000;
				ptval = ((int)printerunits[flag]) * i / cycles[flag];
				if (flag == Y)
					ptval = printerunits[Y] - ptval; //jj
				MoveAbsolute(((flag == X) ? ptval : 0), ((flag == Y) ? ptval : 0));
				SetPenDiameter(3);
				WriteValue(flag, showval, false, pg, vgOutput);
				GenGrid(flag, gs, ge, gd, onecycle[flag], 2, i * onecycle[flag], pg, vgOutput);
				showval *= 10.0;
			}
			ptval = ((int)printerunits[flag]) * i / cycles[flag];
			if (flag == Y)
				ptval = printerunits[Y] - ptval; //jj
			MoveAbsolute(((flag == X) ? ptval : 0), ((flag == Y) ? ptval : 0));
			SetPenDiameter(3);
			WriteValue(flag, showval, false, pg, vgOutput);
		}
		private void WriteValue(int flag, double val, bool SubLogValue, VG.Page pg, IVGOutput vgOutput)
		{
			int size;
			size = GenNumberString(val);
			SavePosition();
			if (flag == X)
			{
				MoveRelative(0, -printerunits[Y]);
				if (!SubLogValue)
					DrawGridRelative(0, (int)(-30 * ConvertToTwips), pg, vgOutput);
				// OLD: MoveRelative((int)((-130 * size / (int)FontPitch) * ConvertToTwips), (int)(-60 * ConvertToTwips));
				MoveRelative((int)((2 - 130 * size / (int)FontPitch) * ConvertToTwips), (int)(-62 * ConvertToTwips));
				if ((AxisLabel[0]) != 0)
				{
					if (SubLogValue)
					{
						StringBuilder sb = new StringBuilder();
						sb.Append(SuperScript[0]);
						sb.Append(valuestring);
						sb.Append(SuperScript[1]);
						valuestring = sb.ToString();
						MoveRelative(30, 30);
					}
					GraphText(valuestring, pg, vgOutput);
				}
			}
			else
			{
				if (!SubLogValue)
					DrawGridRelative((int)(-30 * ConvertToTwips), 0, pg, vgOutput);
				// OLD:  MoveRelative((int)(-(15 + 300 * size / (int)FontPitch) * ConvertToTwips), (int)(-15 * ConvertToTwips));
				MoveRelative((int)(-(14 + 250 * size / (int)FontPitch) * ConvertToTwips), (int)(-16 * ConvertToTwips));
				if ((AxisLabel[1]) != 0)
				{
					if (SubLogValue)
					{
						StringBuilder sb = new StringBuilder();
						sb.Append(SuperScript[0]);
						sb.Append(valuestring);
						sb.Append(SuperScript[1]);
						valuestring = sb.ToString();
						MoveRelative(90, -90); //jj
					}
					GraphText(valuestring, pg, vgOutput);
				}
			}
			RecallPosition();
		}
		private int GenNumberString(double x)
		{
			int power, size, sign;
			double mantissa;
			if (x == 0.0) return (savenumber(x));
			if (x < 0.0)
			{
				x = -x;
				sign = 1;
			}
			else
				sign = 0;
			mantissa = Math.Log10(x);
			power = 0;
			while (mantissa < 0.999)
			{
				power--;
				mantissa++;
			}
			while (mantissa >= 0.999)
			{
				power++;
				mantissa--;
			}
			if (mantissa < 0)
				mantissa = 0;
			if (power > -5 && power < 5)
				size = savenumber(x);
			else
			{
				size = savenumber(Math.Pow(10.0, mantissa));
				size += addpower(power);
				if (valuestring.StartsWith("1x"))
				{
					size -= 2;
					valuestring = valuestring.Remove(0, 2);
				}
			}
			if (sign != 0)
			{
				valuestring = "-" + valuestring;
				size++;
			}
			return (size);
		}
		private int savenumber(double x)
		{
			valuestring = x.ToString("G");
			return valuestring.Length;
		}
		private int addpower(int i)
		{
			int tlen;
			powerstring = string.Format("x10{0}{1}{2}", SuperScript[1], i.ToString(), SuperScript[2]);
			valuestring = valuestring + powerstring;
			tlen = SuperScript[0].Length + SuperScript[1].Length;
			return (powerstring.Length - tlen);
		}
		private void DoAxisTitles(VG.Page pg, IVGOutput vgOutput)
		{
			int len, frommargin;
			string str;
			SavePosition();
			if ((AxisTitles[X] != null) && (len = AxisTitles[X].Length) > 0)
			{
				frommargin = (int)((0.5 * (printerunits[X])) - (0.47 * (len * CWIDEDOTS)));
				// OLD: frommargin = (int)((0.5 * (printerunits[X])) - (0.5 * (len * CWIDEDOTS)));
				MoveAbsolute(frommargin, (3 * CHIGHDOTS) + printerunits[Y]);
				PrintText(AxisTitles[X], pg, vgOutput);
				LinesUsed += (3 * CHIGHDOTS);
				AxisTitles[X] = null;
			}
			if ((AxisTitles[Y] != null) && (len = AxisTitles[Y].Length) > 0)
			{
				if (YLandScape > 0)
				{
					int strIdx = 0;
					MoveTo(YTitleOrigin);
					str = AxisTitles[Y];
					while (strIdx < str.Length)
					{
						PrintText(str[strIdx].ToString(), pg, vgOutput);
						MoveRelative(0, -CHIGHDOTS);
						strIdx++;
					}
				}
				else
				{
					MoveAbsolute(-3 * CWIDEDOTS, -CHIGHDOTS);
					PrintText(AxisTitles[Y], pg, vgOutput);
				}
				AxisTitles[Y] = null;
			}
			RecallPosition();
		}
		private void DoBoxes(VG.Page pg, IVGOutput vgOutput)
		{
			int dx, dy;
			XyBox current;
			string str;
			SavePosition();
			int BoxIdx = 0;
			SetPenDiameter(5);
			while (BoxIdx < AllBoxes.Count)
			{
				current = AllBoxes[BoxIdx];
				MoveTo(current.BoxMinimum);
				dx = current.BoxMaximum.xyValue[X] - current.BoxMinimum.xyValue[X];
				dy = current.BoxMaximum.xyValue[Y] - current.BoxMinimum.xyValue[Y];
				if (current.Shadow != 2) BoxRelative(dx, dy, CurPenWidth, pg, vgOutput);
				if (current.Shadow == 1)
				{
					int savePenWidth = CurPenWidth;
					SavePosition();
					SetPenDiameter(10);
					MoveRelative((int)(10 * ConvertToTwips), -(dy + (int)(5 * ConvertToTwips)));
					DrawRelative(dx, 0, pg, vgOutput);
					MoveRelative(-(int)(3 * ConvertToTwips), -(int)(5 * ConvertToTwips));
					DrawRelative(0, dy, pg, vgOutput);
					CurPenWidth = savePenWidth;
					RecallPosition();
				}
				MoveRelative((CWIDEDOTS * (3 / 2)), -(CHIGHDOTS * (5 / 4)));
				str = current.BoxText;
				int stridx = 0;
				while (stridx < str.Length)
				{
					string tstr = str;
					int idx;
					if ((idx = str.IndexOf("\n", stridx)) >= 0)
					{
						tstr = str.Substring(stridx, idx - stridx);
						stridx = idx + 1;
					}
					PrintText(tstr, pg, vgOutput);
					MoveRelative(0, -CHIGHDOTS);
				}
				BoxIdx++;
			}
			RecallPosition();
		}
		private void DrawLines(VG.Page pg, IVGOutput vgOutput)
		{
			int PlotsIdx = 0;
			DataPoint curpoint;
			PlotLine current;
			SavePosition();
			SetPenDiameter(8);
			/* begin plotting the graph lines, which may be straight or curved */
			while (PlotsIdx < AllPlots.Count)
			{
				current = AllPlots[PlotsIdx];
				int pntListIdx = 0;
				LineDiv = current.PlotDivisions;
				if (current.PlotType == STRAIGHT)
				{			/* straight plot */
					curpoint = current.PlotDataPoints[pntListIdx];
					MoveTo(curpoint.APoint);
					while (++pntListIdx < current.PlotDataPoints.Count)
					{
						curpoint = current.PlotDataPoints[pntListIdx];
						DrawLineTo(curpoint.APoint, pg, vgOutput);
					}
				}
				else
				{
					DrawCurve(current, pg, vgOutput);						/* curved plot */
				}
				PlotsIdx++;
			}  /* repeat for each plot (while) */
			RecallPosition();
		}
		private point FindCenterOfArc(point StartPoint, double Slope1, point EndPoint, double Slope2)
		{
			point center = new point();
			double s1, s2;
			int x1, y1, x2, y2;
			center.xyValue = new int[Dimensions];
			s1 = tan_deg(FixAngle(atan_deg(Slope1)));
			s2 = tan_deg(FixAngle(atan_deg(Slope2)));
			x1 = StartPoint.xyValue[X];
			y1 = StartPoint.xyValue[Y];
			x2 = EndPoint.xyValue[X];
			y2 = EndPoint.xyValue[Y];
			center.xyValue[X] = (int)(((s2 * s1) * (y2 - y1) + (s1 * x2) - (s2 * x1)) / (s1 - s2));
			if (s1 == 0.0)
				center.xyValue[Y] = (int)(y2 - ((center.xyValue[X] - x2) / s2));
			else
				center.xyValue[Y] = (int)(y1 - ((center.xyValue[X] - x1) / s1));
			return (center);
		}
		private point FindModifiedCenter(double rprime, point PtOnArc, double ang)
		{
			point newcenter = new point();
			newcenter.xyValue = new int[Dimensions];
			newcenter.xyValue[X] = (int)(PtOnArc.xyValue[X] - rprime * cos_deg(ang));
			newcenter.xyValue[Y] = (int)(PtOnArc.xyValue[Y] - rprime * sin_deg(ang));
			return (newcenter);
		}
		private int AddMidPoint(DataPoints ptLstAry, int pt1Idx, int pt2Idx)
		{
			DataPoint midpt;
			DataPoint pt1, pt2;
			point pt;
			double so;
			int x1, y1, x2, y2;
			pt1 = ptLstAry[pt1Idx];
			pt2 = ptLstAry[pt2Idx];
			x1 = pt1.APoint.xyValue[X];
			y1 = pt1.APoint.xyValue[Y];
			x2 = pt2.APoint.xyValue[X];
			y2 = pt2.APoint.xyValue[Y];
			midpt = new DataPoint();
			pt = new point();
			pt.xyValue = new int[Dimensions];
			so = FindSlope(pt1.APoint, pt2.APoint);
			midpt.slope = (4.0 * so - pt1.slope - pt2.slope) / 2.0;
			pt.xyValue[X] = x1 + (x2 - x1) / 2;
			pt.xyValue[Y] = y1 + (y2 - y1) / 2;
			midpt.APoint = pt;
			ptLstAry.Insert(pt1Idx + 1, midpt);
			return (pt1Idx + 1);
		}
		private double FindTheta(point Endpoint, point Centerpoint)
		{
			double dy, dx, result;
			dx = Endpoint.xyValue[X] - Centerpoint.xyValue[X];
			dy = Endpoint.xyValue[Y] - Centerpoint.xyValue[Y];
			result = 90.0 - atan_deg((dy / dx));
			if (dx < 0) result += 180.0;
			return (result);
		}
		private double FindRadius(point Endpoint, point Centerpoint)
		{
			double dy, dx, r;
			dx = Centerpoint.xyValue[X] - Endpoint.xyValue[X];
			dy = Centerpoint.xyValue[Y] - Endpoint.xyValue[Y];
			r = Math.Sqrt(((dx * dx) + (dy * dy)));
			return (r);
		}
		private void DrawCurve(PlotLine linestart, VG.Page pg, IVGOutput vgOutput)
		{
			int pltptListIdx = -1;
			while (++pltptListIdx < linestart.PlotDataPoints.Count - 1)
			{
				DoSubArcs(linestart.PlotDataPoints, pltptListIdx, pg, vgOutput);
			}
		}
		private double GetAngle(double s1)
		{
			double t1;
			t1 = atan_deg(s1);
			return (t1);
		}
		private double GetAngleBetweenPoints(point pt1, point pt2)
		{
			double retval;
			retval = GetAngle(FindSlope(pt1, pt2));
			if (pt2.xyValue[X] < pt1.xyValue[X])
			{
				if (retval < 0)
					retval += 180;
				else
					retval -= 180;
			}
			return (retval);
		}
		private double SpecialGetAngleBetweenPoints(point pt1, point pt2, double s1)
		{
			double retval;
			if (s1 == 0.0)
			{
				if (pt1.xyValue[Y] < pt2.xyValue[Y])
					s1 = Convert.ToDouble("1.E99");
				else
					s1 = Convert.ToDouble("-1.E99");
			}
			else
				s1 = -1 / s1;
			retval = GetAngle(s1);
			if (pt2.xyValue[X] < pt1.xyValue[X])
			{
				if (retval < 0)
					retval += 180;
				else
					retval -= 180;
			}
			return (retval);
		}
		private double GetArcAngle(double s1, double s2)
		{
			double result, t1, t2;
			t1 = GetAngle(s1);
			t2 = GetAngle(s2);
			result = t1 - t2;
			return (result);
		}
		private void DoSubArcs(DataPoints dataPoints, int aryIdx, VG.Page pg, IVGOutput vgOutput)
		{
			bool debug = false; //jsj
			int x, y, x1, x2, y1, y2;
			DataPoint pt1 = dataPoints[aryIdx];
			DataPoint pt2 = dataPoints[aryIdx + 1];
			DebugOutput("pt1={0},{1},{2}", pt1.APoint.xyValue[0], pt1.APoint.xyValue[1], pt1.slope);
			DebugOutput("pt2={0},{1},{2}", pt2.APoint.xyValue[0], pt2.APoint.xyValue[1], pt2.slope);
			double theta, delta1, delta2, r1, r2, t1, t2, theta1, theta2, thetaa, t3, t4;
			double c1, c2, s1, s2;
			double t3a, t4a;
			point cp1, cp2;
			t3 = FixAngle(t3a=GetAngle(pt1.slope));
			t4 = FixAngle(t4a=GetAngle(pt2.slope));
			thetaa = GetAngleBetweenPoints(pt1.APoint, pt2.APoint);
			if ((t3 - thetaa) * (t4 - thetaa) >= 0.0)
			{
				double tprime;
				tprime = FixAngle(thetaa);
				if (Math.Abs(tprime - t3) < ANGLELIMIT)
				{
					MoveTo(pt1.APoint);
					DrawTo(pt2.APoint, pg, vgOutput);
					return;
				}
				else
				{
					pt2 = dataPoints[AddMidPoint(dataPoints, aryIdx, aryIdx + 1)];
					t4 = FixAngle(GetAngle(pt2.slope));
					thetaa = GetAngleBetweenPoints(pt1.APoint, pt2.APoint);
				}
			}
			if (thetaa < t3)
				t1 = t3 + 90;
			else
				t1 = t3 - 90;
			if (thetaa > t4)
				t2 = t4 + 90;
			else
				t2 = t4 - 90;
			theta = t2 - t1;
			theta1 = FixAngle(thetaa - t3);
			theta2 = theta - theta1;
			if (Math.Abs(theta1) < Math.Abs(theta2))
			{
				if (Math.Abs(theta1) < ANGLELIMIT)
				{
					theta1 = 0;
					theta2 = theta;
				}
			}
			else if (Math.Abs(theta2) < Math.Abs(theta1))
			{
				if (Math.Abs(theta2) < ANGLELIMIT)
				{
					theta2 = 0;
					theta1 = theta;
				}
			}
			x1 = pt1.APoint.xyValue[X];
			y1 = pt1.APoint.xyValue[Y];
			x2 = pt2.APoint.xyValue[X];
			y2 = pt2.APoint.xyValue[Y];
			s1 = sin_deg(t1 + 180);
			s2 = sin_deg(t2 + 180);
			c1 = cos_deg(t1 + 180);
			c2 = cos_deg(t2 + 180);
			if (s1 == 0.0)
			{
				r2 = (x1 - x2) / s2;
				r1 = (y2 + r2 * c2 - y1) / c1;
			}
			else if (s2 == 0.0)
			{
				r1 = (x2 - x1) / s1;
				r2 = (y1 + r1 * c1 - y2) / c2;
			}
			else if (c1 == 0.0)
			{
				r2 = (y1 - y2) / c2;
				r1 = (x2 + r2 * s2 - x1) / s1;
			}
			else if (c2 == 0.0)
			{
				r1 = (y2 - y1) / c1;
				r2 = (x1 + r1 * s1 - x2) / s2;
			}
			else
			{
				r2 = (((x2 - x1) / c1) - ((y2 - y1) / s1)) / (s2 / s1 - c2 / c1);
				r1 = (y2 - y1 + r2 * s2) / s1;
			}
			MoveTo(pt1.APoint);
			if (debug) //jsj
			{
				DrawTo(pt2.APoint, pg, vgOutput);
				return;
			}
			if (Math.Abs(theta1) > ANGLELIMIT && Math.Abs(theta2) > ANGLELIMIT)
			{
				delta1 = (r2 - r1) / (
					(1.0 - cos_deg(theta1)) +
					(1.0 - cos_deg(theta2)) * sin_deg(theta1) / sin_deg(theta2));
				r1 = Math.Floor(0.5 + r1 + delta1);
				cp1 = FindModifiedCenter(r1, pt1.APoint, t1);
				x = (int)(cp1.xyValue[X] + r1 * cos_deg(t1 + theta1));
				y = (int)(cp1.xyValue[Y] + r1 * sin_deg(t1 + theta1));
				delta2 = delta1 * sin_deg(theta1) / sin_deg(theta2);
				r2 = Math.Floor(0.5 + r2 - delta2);
				cp2 = FindModifiedCenter(r2, pt2.APoint, t2);
				DebugOutput("DrawArc1"); VG_Arc.iColor = 1;
				DrawArc(r1, t1, t1 + theta1, cp1, x, y, pg, vgOutput);
				DebugShowLocation("DrawArc1");
				MoveTo(pt2.APoint);
				DebugShowLocation("MoveTo1");
				if (r2 > 15000)
				{
					DebugOutput("DrawAbs1 {0},{1}", x, y);
					DrawAbsolute(x, y, pg, vgOutput);
					DebugShowLocation("DrawAbs1");
				}
				else
				{
					DebugOutput("DrawArc2"); VG_Arc.iColor = 2;
					DrawArc(r2, t2, t2 - theta2, cp2, x, y, pg, vgOutput);
					DebugShowLocation("DrawArc2");
				}
			}
			else if (Math.Abs(theta1) > ANGLELIMIT)
			{
				delta1 = (r2 - r1) / (1.0 - cos_deg(theta1));
				r1 = Math.Floor(0.5 + r1 + delta1);
				if (r1 < 0) r1 = -r1;
				cp1 = FindModifiedCenter(r1, pt1.APoint, t1);
				x = (int)(cp1.xyValue[X] + r1 * cos_deg(t1 + theta1));
				y = (int)(cp1.xyValue[Y] + r1 * sin_deg(t1 + theta1));
				if (r1 > 15000)
				{
					DebugOutput("DrawAbs2");
					DrawAbsolute(x, y, pg, vgOutput);
					DebugShowLocation("DrawAbs2");
				}
				else
				{
					//Console.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}", t3, t3a, t4, t4a, thetaa, t1, t2, r1, r2, aryIdx);
					DebugOutput("DrawArc3"); VG_Arc.iColor = 3;
					VG_Arc.iColor = 4;
					DrawArc(r1, t1, t1 + theta1, cp1, x, y, pg, vgOutput);
					//Console.WriteLine("'DrawArc3','{0}',{1},{2}", Key, r1, theta1);
					DebugShowLocation("DrawArc3");
				}
				MoveAbsolute(x, y);
				DebugShowLocation("MoveAbs2");
				DebugOutput("DrawTo1");
				DrawTo(pt2.APoint, pg, vgOutput);
				DebugShowLocation("DrawTo1");
				//DebugOutput("DrawArc4"); VG_Arc.iColor = 4;
				//DrawArc(r1, t1, t1 + theta1, cp1, x, y, pg, vgOutput);
				//DebugShowLocation("DrawArc4");
			}
			else if (Math.Abs(theta2) > ANGLELIMIT)
			{
				delta2 = (r2 - r1) / (1.0 - cos_deg(theta2));
				r2 = Math.Floor(0.5 + r2 - delta2);
				if (r2 < 0) r2 = -r2;
				cp2 = FindModifiedCenter(r2, pt2.APoint, t2);
				x = (int)(cp2.xyValue[X] + r2 * cos_deg(t2 - theta2));
				y = (int)(cp2.xyValue[Y] + r2 * sin_deg(t2 - theta2));
				DebugOutput("DrawAbs3");
				DrawAbsolute(x, y, pg, vgOutput);
				DebugShowLocation("DrawAbs3");
				MoveTo(pt2.APoint);
				DebugShowLocation("MoveTo1");
				if (r2 > 15000)
				{
					DebugOutput("DrawAbs4");
					DrawAbsolute(x, y, pg, vgOutput);
					DebugShowLocation("DrawAbs4");
				}
				else
				{
					DebugOutput("DrawArc5"); VG_Arc.iColor = 5;
					VG_Arc.iColor = 6;
					DrawArc(r2, t2, t2 - theta2, cp2, x, y, pg, vgOutput);
					DebugShowLocation("DrawArc5");
				}
				//DebugOutput(""); VG_Arc.iColor = 6;
				//DrawArc(r2, t2, t2 - theta2, cp2, x, y, pg, vgOutput);
				//DebugShowLocation("DrawArc6");
			}
			else
			{
				DebugOutput("DrawTo2");
				DrawTo(pt2.APoint, pg, vgOutput);
				DebugShowLocation("DrawTo2");
				pt2.slope = FindSlope(pt1.APoint, pt2.APoint);
			}
			return;
		}
		private void SetPenDiameter(int pd)
		{
			if ((pd * ConvertToTwips) != CurPenWidth)
			{
				CurPenWidth = (int)(pd * ConvertToTwips);
			}
		}
		private void PrintStackError(string relation, int limit)
		{
			MessageBox.Show(String.Format("Position Stack Pointer {0} {1}", relation, limit.ToString()), "Print Stack Error");
		}
		private void SavePosition()
		{
			if (stack >= MAX_XY_STACK) PrintStackError(">=", MAX_XY_STACK);
			SavedX[stack] = Position[X];
			SavedY[stack++] = Position[Y];
		}
		private void RecallPosition()
		{
			if (stack <= 0) PrintStackError(" <", 0);
			Position[X] = SavedX[--stack];
			Position[Y] = SavedY[stack];
		}
		private void MoveTo(point pnt)
		{
			MoveAbsolute(pnt.xyValue[X], pnt.xyValue[Y]);
		}
		private void DrawTo(point pnt, VG.Page pg, IVGOutput vgOutput)
		{
			DrawAbsolute(pnt.xyValue[X], pnt.xyValue[Y], pg, vgOutput);
		}
		private void MoveAbsolute(int ptx, int pty)
		{
			int x, y;
			x = ptx - Position[X];
			y = Position[Y] - pty;
			MoveRelative(x, y);
		}
		private void DrawAbsolute(int ptx, int pty, VG.Page pg, IVGOutput vgOutput)
		{
			int x, y;
			x = ptx - Position[X];
			y = Position[Y] - pty;
			DrawRelative(x, y, pg, vgOutput);
		}
		private void DrawGridAbsolute(int ptx, int pty, VG.Page pg, IVGOutput vgOutput)
		{
			int x, y;
			x = ptx - Position[X];
			y = Position[Y] - pty;
			DrawGridRelative(x, y, pg, vgOutput);
		}
		private void relval(string ptr, int i)
		{
			ptr = string.Format("+{0}", i.ToString());
		}
		private void MoveRelative(int x, int y)
		{
			Position[X] += x;
			Position[Y] -= y;
		}
		private void DrawRelative(int x, int y, VG.Page pg, IVGOutput vgOutput)
		{
			if (Visible == 0)
				MoveRelative(x, y);
			else
			{
				int toX, toY;
				toX = Position[X] + x;
				toY = Position[Y] - y;
				VG_Line RelLine = new VG_Line(Position[X], Position[Y], toX, toY, CurPenWidth, pg);
				Position[X] += x;
				Position[Y] -= y;
				RelLine.Draw(vgOutput);
			}
		}
		private void DrawGridRelative(int x, int y, VG.Page pg, IVGOutput vgOutput)
		{
			if (Visible == 0)
				MoveRelative(x, y);
			else
			{
				DrawRelative(x, y, pg, vgOutput);
			}
		}
		private void DrawArc(double radius, double Sangle, double Eangle, point cp, int x, int y, VG.Page pg, IVGOutput vgOutput)
		{
			DebugOutput("r={0} sAngle={1} eAngle={2} cp={3},{4} x={5} y={6}", radius, Sangle, Eangle, cp.xyValue[0], cp.xyValue[1], x, y);
			int dx, dy;
			long radchk;
			radchk = (long)radius;
			if (radchk > 72000L)
			{
				DrawAbsolute(x, y, pg, vgOutput);
				return;
			}
			dx = x - cp.xyValue[X]; //jj
			dy = Math.Abs(cp.xyValue[Y] - Position[Y]); //jj
			float x1 = cp.xyValue[X] - (float)radius;
			float y1 = cp.xyValue[Y] - (float)radius;
			float w1 = (float)radius * 2;
			VG_Arc arc = new VG_Arc(x1, y1, w1, w1, (float)Sangle, (float)(Eangle - Sangle), CurPenWidth, pg);
			DebugOutput("arc {0},{1},{2},{3},{4},{5}", x1, y1, w1, w1, (float)Sangle, (float)(Eangle - Sangle));
			arc.Draw(vgOutput);
		}
		private void DrawGridLine(int flag, int val, VG.Page pg, IVGOutput vgOutput)
		{
			XyBox curbox;
			int v1, v2;
			int BoxesIdx = 0;
			FindBoxesinArea(val, (flag == 0) ? 1 : 0);
			v1 = Position[flag];
			if (ActiveBoxes.Count > 0)
			{
				curbox = ActiveBoxes[BoxesIdx];
				while (BoxesIdx < ActiveBoxes.Count)
				{
					int my_x, my_y;
					v2 = curbox.BoxMinimum.xyValue[flag];
					my_x = (flag == X) ? ((v2 < printerunits[flag]) ? v2 : printerunits[flag])
						: val;
					my_y = (flag == Y) ?
						((v2 < printerunits[flag]) ? v2 : printerunits[flag])
						: val;
					DrawGridAbsolute(my_x, my_y, pg, vgOutput);
					v1 = curbox.BoxMaximum.xyValue[flag];
					while ((BoxesIdx < ActiveBoxes.Count) && (curbox.BoxMinimum.xyValue[flag] < v1))
					{
						v1 = (curbox.BoxMaximum.xyValue[flag]);
						BoxesIdx++;
						if (BoxesIdx < ActiveBoxes.Count)
							curbox = ActiveBoxes[BoxesIdx];
					}
					MoveAbsolute((flag == X ? v1 : val), (flag == Y ? v1 : val));
				}
			}
			v2 = printerunits[flag];
			if (v1 < v2) DrawGridAbsolute((flag == X ? v2 : val), (flag == Y ? v2 : val), pg, vgOutput);
			ActiveBoxes.Clear();
		}
		private void LogScale(int flag)
		{
			double newminimum, newmaximum, maxlimit = maximum[flag] * .9;
			newminimum = 1.0;
			while (newminimum < minimum[flag]) newminimum *= 10.0;
			while (newminimum > minimum[flag]) newminimum /= 10.0;
			newmaximum = 10.0 * newminimum;
			cycles[flag] = 1;
			while (newmaximum < maxlimit)
			{
				newmaximum *= 10.0;
				cycles[flag]++;
			}
			onecycle[flag] = printerunits[flag] / cycles[flag];
			minimum[flag] = newminimum;
			maximum[flag] = newmaximum;
		}
		private void LinearScale(int flag)
		{
			double mantissa, nx;
			double newminimum, newmaximum;
			int power = 0;
			if (spcminor[flag] == 0)
				minor[flag] = 10;
			else
				minor[flag] = spcminor[flag];
			if (delta[flag] == 0)
			{
				delta[flag] = maximum[flag] - minimum[flag];
				mantissa = Math.Log10(delta[flag]);
				while (mantissa < 1.2)
				{
					mantissa++;
					power--;
				}
				while (mantissa > 1.2)
				{
					mantissa--;
					power++;
				}
				if (mantissa < 0) mantissa = 0;
				nx = Math.Pow(10.0, mantissa);
				if (nx > 5.0)
				{
					delta[flag] = 1.0;
				}
				else if (nx > 3.001)
				{		// allow for roundoff
					delta[flag] = 0.5;
				}
				else if (nx > 2.001)
				{		// allow for roundoff
					delta[flag] = 0.25;
				}
				else
				{
					delta[flag] = 0.2;
					if (spcminor[flag] == 0) minor[flag] = 8;
				}
				while (power > 0)
				{
					power--;
					delta[flag] *= 10.0;
				}
				while (power < 0)
				{
					power++;
					delta[flag] /= 10.0;
				}
			}
			if (GridOnFlag == 0 || GridOnFlag == 1)
				minor[flag] = 1;
			else if (GridOnFlag == 2)
				minor[flag] /= 2;
			newminimum = ((int)(0.01 + minimum[flag] / delta[flag])) * delta[flag];
			if (newminimum > minimum[flag] + (delta[flag] * 0.001))
				newminimum -= delta[flag];
			newmaximum = ((int)(maximum[flag] / delta[flag])) * delta[flag];
			if (newmaximum < maximum[flag] - (delta[flag] * 0.001))
				newmaximum += delta[flag];
			minimum[flag] = newminimum;
			maximum[flag] = newmaximum;
		}
		private void err_exit(string msg, string msg2)
		{
			MessageBox.Show(msg + "\n" + msg2 + "\n\n This p=Process Will Terminate", "\n## ERROR ## - ");
			Environment.Exit(255);
		}
		private void FindBoxesinArea(int ptval, int flag)
		{
			XyBox cptr;
			int BoxesIdx = 0;
			while (BoxesIdx < AllBoxes.Count)
			{
				cptr = AllBoxes[BoxesIdx];
				if (cptr.BoxMinimum.xyValue[flag] <= ptval &&
					cptr.BoxMaximum.xyValue[flag] >= ptval)
					AddBoxToActiveList(cptr);
				BoxesIdx++;
			}
		}
		private void AddBoxToActiveList(XyBox box)
		{
			int i = 0;
			while (i < ActiveBoxes.Count)
			{
				if (box.BoxMinimum.xyValue[DrawDirection] < ActiveBoxes[i].BoxMinimum.xyValue[DrawDirection]) 
					break; // Located the location to insert the box
				i++;
			}
			ActiveBoxes.Insert(i, box);
		}
		private void GenGrid(int flag, int ss, int ee, int dd, int w, int lw, int goffset, VG.Page pg, IVGOutput vgOutput)
		{
			int val, wn, valn;
			double p2;
			int n, dn, MinorGrid = 0;
			if ((GridOnFlag * lw) <= 1) return;
			if (ee / ss == 10) MinorGrid = 2;
			n = ss;
			p2 = n;
			w = (int)(w / Math.Log10(ee / p2)); //jj
			val = (int)(Math.Log10(p2 / ss) * w + goffset);
			MoveAbsolute((flag == X ? val : 0), (flag == Y ? val : 0));
			DrawGridLine((flag == 0) ? 1 : 0, val, pg, vgOutput);
			if ((GridOnFlag * lw) == 2) return;
			SetPenDiameter(lw);
			while (n < ee)
			{
				n += dd;
				p2 = n;
				valn = (int)(Math.Log10(p2 / ss) * w + goffset);
				wn = valn - val;
				if (wn > DPI / 20 && lw > 1)
				{
					if (wn > DPI * 2 / 5) dn = dd / 10;
					else if (wn > DPI / 5) dn = dd / 5;
					else dn = dd / 2;
					GenGrid(flag, n - dd, n, dn, wn, lw - 1, val, pg, vgOutput);
					wn = 0;
				}
				val = valn;
				if (flag == Y)
					val = printerunits[Y] - val; //jj
				MoveAbsolute((flag == X ? val : 0), (flag == Y ? val : 0));
				if ((MinorGrid != 0) && (MinorGrid < 10))
				{
					WriteValue(flag, MinorGrid++, true, pg, vgOutput); //these numbers are in dots
				}
				DrawGridLine((flag == 0) ? 1 : 0, val, pg, vgOutput);
			}
		}
		private void PrintText(string txt, VG.Page pg, IVGOutput vgOutput)
		{
			StringBuilder tstr = new StringBuilder(txt);
			int ptr;
			SavePosition();	/* 'cause TEXT...,E screws up position! */
			ptr = 0;
			while (ptr < tstr.Length)
			{
				if ("`øòó".IndexOf(tstr[ptr]) >= 0)
				{
					string SpecialChar = "";
					switch ((byte)tstr[ptr])
					{
						case 0x60:					/* backquote */
						case 0xF8: SpecialChar = "\u00B0";	/* degree symbol */
							break;
						case 0xF2: SpecialChar = "\u2265";	/* greaterthanequal */
							break;
						case 0xF3: SpecialChar = "\u2264";	/* lessthanequal */
							break;
					}
					tstr.Remove(ptr, 1);
					tstr.Insert(ptr, SpecialChar);
					ptr++;
				}
				else
				{
					ptr++;
				}
			}
			GraphText(tstr.ToString(), pg, vgOutput);
			RecallPosition();
		}
		private void FreeBoxList()
		{
			AllBoxes.Clear();
		}
		private void FreeLineList()
		{
			AllPlots.Clear();
		}
		private void Init_Graphic_Vars()
		{
			int i;
			SavedX = new int[MAX_XY_STACK];
			SavedY = new int[MAX_XY_STACK];
			LinesUsed = 0;		/* Number lines used by graph, in dots */
			Visible = 1;
			ShadowFlag = 0;
			CurPenWidth = 0;
			DrawDirection = -1;
			AxisTitles[X] = null;
			AxisTitles[Y] = null;
			AxisLabel[0] = 1;
			AxisLabel[1] = 1;
			EndFlag = 0;
			for (i = 0; i < Dimensions; i++)
			{
				Scale[0, i] = 0;
				Scale[1, i] = 0;
				Offset[0, i] = 0;
				Offset[1, i] = 0;
				delta[i] = 0;
				cycles[i] = 0;
				onecycle[i] = 0;
				minor[i] = 0;
			}
		}
		private void DrawLineTo(point pnt, VG.Page pg, IVGOutput vgOutput)
		{
			int x1, y1, x2, y2;
			int nd, x, y, i;
			long ddx, ddy, dx, dy;
			x1 = Position[X];
			y1 = Position[Y];
			x2 = pnt.xyValue[X];
			y2 = pnt.xyValue[Y];
			if (LineDiv != 0)
			{
				nd = LineDiv * 4;
				dx = x2 - x1;
				dy = y2 - y1;
				for (i = 1; i <= nd; i++)
				{
					ddx = dx * i;
					ddy = dy * i;
					x = (int)(x1 + ddx / nd);
					y = (int)(y1 + ddy / nd);
					if ((i & 2) != 0)
						MoveAbsolute(x, y);
					else
						DrawAbsolute(x, y, pg, vgOutput);
				}
			}
			else
				DrawTo(pnt, pg, vgOutput);
		}
		private void BoxRelative(int dx, int dy, int LineWidth, VG.Page pg, IVGOutput vgOutput)
		{
			VG_Rect vgrec = new VG_Rect((int)Position[X], (int)Position[Y], dx, dy, LineWidth, pg);
			vgrec.Draw(vgOutput);
		}
		private void GraphText(string txt, VG.Page pg, IVGOutput vgOutput)
		{
			// Look for a Superscript or Subsript.
			// - break up the string (if needed) and change the font pitch
			//   for the text that is to be super/subscripted
			VG_Text drawText;
			string tstr = "";
			int Xpos, Ypos;
			int sidx = 0; // beginning of string
			int idx = 0; // current position in string
			Xpos = Position[X];
			Ypos = Position[Y];
			while (idx < txt.Length)
			{
				int tidx;
				if ((tidx = txt.IndexOf(SuperScript[0], idx)) > -1)
				{
					// print text up to the superscript
					tstr = txt.Substring(sidx, tidx - sidx);
					drawText = new VG_Text(Xpos, Ypos, (int)FontPitch, tstr, MyFontFamily, "", "", "", pg);
					drawText.Draw(vgOutput);
					Xpos += (CWIDEDOTS * tstr.Length);
					tidx += SuperScript[0].Length;
					idx = tidx;
					sidx = idx;
					tidx = txt.IndexOf(SuperScript[1], idx);
					if (tidx > 0)
					{
						// switch font pitch, move up some, and print the superscript text
						tstr = txt.Substring(sidx, tidx - sidx);
						Ypos -= (CHIGHDOTS / 2); // move up half a char heigth
						drawText = new VG_Text(Xpos, Ypos, (int)(FontPitch / 2), tstr, MyFontFamily, "", "", "", pg);
						drawText.Draw(vgOutput);
						Ypos += (CHIGHDOTS / 2); // move back down
						Xpos += ((CWIDEDOTS / 2) * tstr.Length); // positon half the width of the string
						tidx += SuperScript[1].Length;
						idx = tidx;
						sidx = idx;
						continue; // go back to beginning of while loop
					}
				}
				else if ((tidx = txt.IndexOf(SubScript[0], idx)) > -1)
				{
					// print text up to the superscript
					tstr = txt.Substring(sidx, tidx - sidx);
					drawText = new VG_Text(Xpos, Ypos, (int)FontPitch, tstr, MyFontFamily, "", "", "", pg);
					drawText.Draw(vgOutput);
					Xpos += (CWIDEDOTS * tstr.Length);
					tidx += SubScript[0].Length;
					idx = tidx;
					sidx = idx;
					tidx = txt.IndexOf(SubScript[1], idx);
					if (tidx > 0)
					{
						// switch font pitch, move up some, and print the superscript text
						tstr = txt.Substring(sidx, tidx - sidx);
						drawText = new VG_Text(Xpos, Ypos, (int)(FontPitch / 2), tstr, MyFontFamily, "", "", "", pg);
						drawText.Draw(vgOutput);
						Xpos += ((CWIDEDOTS / 2) * tstr.Length); // positon half the width of the string
						tidx += SubScript[1].Length;
						idx = tidx;
						sidx = idx;
						continue; // go back to beginning of while loop
					}
				}
				else
				{
					idx++;
				}
			} // end while
			if (sidx < txt.Length)
			{
				tstr = txt.Substring(sidx);
				drawText = new VG_Text(Xpos, Ypos, (int)FontPitch, tstr, MyFontFamily, "", "", "", pg);
				drawText.Draw(vgOutput);
			}
		}
		/// 
		/// Generates a vgOutput files containing an X/Y PLot generated from the passed in
		/// commands.
		/// 
		/// Plot Commands
		public XYPlot(string PlotCommands)
		{
			LoadBuffFromString(PlotCommands);
			Setup();
			VG.VG_Arc.iColor = 0;
		}
		public void Setup()
		{
			char Command, PrevCommand = ' ';
			Init_Graphic_Vars();
			FixBuffIfNeeded();
			GetScaleInfo();
			while ((Command = char.ToUpper(getchr())) != '\0')
			{
				LineDiv = 0;
				LineFlag = CURVE;
				ShadowFlag = 0;
				switch (Command)
				{
					case 'T':
						ShadowFlag += 2; // 2 for no box
						GetBoxParameters();
						break;
					case 'W':
						ShadowFlag++; // 1 for shadow box
						GetBoxParameters();
						break;
					case 'B':
						GetBoxParameters(); // 0 for box
						break;
					case 'D':
						LineDiv = getint();
						LineFlag = STRAIGHT;
						GetLineParameters();
						break;
					case 'L':
						LineFlag = STRAIGHT;
						GetLineParameters();
						break;
					case 'C':
						GetLineParameters();
						break;
					case 'X':
						GetAxisTitle(X);
						break;
					case 'Y':
						GetAxisTitle(Y);
						break;
					case 'Z':
						YLandScape = 1;
						YTitleOrigin = GetPair(USER);
						GetAxisTitle(Y);
						break;
					case 'A':
						AxisLabel[0] = getint();
						AxisLabel[1] = getint();
						break;
					default:
						if (PrevCommand.Equals(null))
							MessageBox.Show("Check the first line of the X/Y Plot definition.", "Unrecognized Graph Command");
						else
							MessageBox.Show(string.Format("Problem with the X/Y Plot Command after {0}", PrevCommand.ToString()), "Unrecognized Graph Command");
						break;
				}
				PrevCommand = Command;
			}
		}
		public void Process(IVGOutput vgOutput)
		{
			VG.Page pg = new Page(true, _LeftMargin, _TopMargin, _RightMargin, _BottomMargin);
			GenerateGrid(pg, vgOutput);
			DoAxisTitles(pg, vgOutput);
			DoBoxes(pg, vgOutput);
			DrawLines(pg, vgOutput);
			FreeBoxList();
			FreeLineList();
			CloseGraph();
		}
		private void ShowPoints(DataPoints dataPoints)
		{
#if DEBUG
			foreach (DataPoint pl in dataPoints)
			{
				DebugOutput("x={0},y={1},slope={2}", pl.APoint.xyValue[0], pl.APoint.xyValue[1], pl.slope);
			}
#endif
		}
		public static string Key = "";
		private void DebugOutput(string format, params object[] args)
		{
#if DEBUG
			//if (args.Length == 0)
			//	Console.WriteLine("'{0}','{1}'",Key,format);
			//Console.WriteLine(format, args);
#endif
		}
		private void DebugShowLocation(string command)
		{
			//Console.WriteLine("'{0}',{1},{2}", command, Position[X], Position[Y]);
		}
	}
}