效果如图所示:
以下是代码:
- public class Curve
- {
- //picture
- Bitmap retbit = new Bitmap(800, 600);
- //
- Font _textFont;
- //x,y value in actual
- float[] x;
- float[] y;
- //x,y scale in the axies
- float[] scale_X;
- float[] scale_Y;
- float X_Max;
- float Y_Max;
- float X_Min;
- float Y_Min;
- //x,y与图标长度的换算值
- float ratio_X;
- float ratio_Y;
- //起始刻度
- float ScaleX_Min;
- float ScaleX_Max;
- float ScaleY_Min;
- float ScaleY_Max;
- //
- int stepes_X;
- int stepes_Y;
- //
- float stepSize_X;
- float stepSize_Y;
- float range_X;
- float range_Y;
- //
- PointF[] drawPts;
- //刻度文字
- string[] text_X;
- string[] text_Y;
- //
- SizeF lengthSizeText;
- //curve zone backcolor
- Color backColor_Curve;
- //
- float curvewidth;
- float curveheight;
- Pen arrowPen = new Pen(Color.Black, 1);
- //constructors
- public Curve()
- {
- }
- public Curve(PointF[] points)
- {
- x = new float[points.Length];
- y = new float[points.Length];
- for (int i = 0; i < x.Length; i++)
- {
- x[i] = points[i].X;
- y[i] = points[i].Y;
- }
- InitializePoints();
- DrawCurve();
- }
- public Curve(float[] x, float[] y)
- {
- }
- //
- private void InitializePoints()
- {
- scale_X = new float[x.Length];
- scale_Y = new float[x.Length];
- drawPts = new PointF[x.Length];
- //
- _textFont = new Font("宋体", 10);
- //
- ArrayList tempArray = new ArrayList();
- X_Max = x[0]; Y_Max = y[0];
- X_Min = x[0]; Y_Min = y[0];
- for (int i = 1; i < x.Length; i++)
- {
- X_Max = Math.Max(X_Max, x[i]);
- X_Min = Math.Min(X_Min, x[i]);
- Y_Max = Math.Max(Y_Max, y[i]);
- Y_Min = Math.Min(Y_Min, y[i]);
- }
- range_X = X_Max - X_Min;
- range_Y = Y_Max - Y_Min;
- backColor_Curve = Color.LightGray;
- }
- //
- protected void DrawCurve()
- {
- Graphics g = Graphics.FromImage(retbit);
- g.FillRectangle(Brushes.LightGray, new Rectangle(0, 0, retbit.Width, retbit.Height));
- arrowPen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
- arrowPen.CustomEndCap = new System.Drawing.Drawing2D.AdjustableArrowCap(arrowPen.Width * 3, arrowPen.Width * 5);
- Pen pen = new Pen(Color.Red);
- //计算y的起始刻度值
- int tmpInt;
- stepSize_Y = (float)CalcStepSize(range_Y, 10);
- //y 最小刻度值
- tmpInt = (int)(Y_Min / stepSize_Y);
- if (tmpInt < 0)
- tmpInt = tmpInt - 1;
- ScaleY_Min = tmpInt * stepSize_Y;
- //y最大刻度值
- if (Y_Max % stepSize_Y == 0)
- tmpInt = (int)(Y_Max / stepSize_Y);
- else
- tmpInt = (int)(Y_Max / stepSize_Y + 1);
- ScaleY_Max = tmpInt * stepSize_Y;
- stepes_Y = (int)((ScaleY_Max - ScaleY_Min) / stepSize_Y)+1;
- //计算x的起始刻度值
- stepSize_X = (float)CalcStepSize(range_X, 10);
- //x最小刻度
- tmpInt = (int)(X_Min / stepSize_X);
- if (tmpInt < 0)
- tmpInt = tmpInt - 1;
- ScaleX_Min = tmpInt * stepSize_X;
- //x最大刻度
- if (X_Max % stepSize_X == 0)
- tmpInt = (int)(X_Max / stepSize_X);
- else tmpInt = (int)(X_Max / stepSize_X + 1);
- ScaleX_Max = tmpInt * stepSize_X;
- stepes_X = (int)((ScaleX_Max - ScaleX_Min) / stepSize_X)+1;
- //
- float offset_X = 0;
- float offset_Y = 0;
- lengthSizeText = SetText(g,ref offset_X,ref offset_Y);
- //图标距离顶部的高度,
- //预留的坐标轴箭头空间
- float curve_Top = lengthSizeText.Height/2;
- float curve_Right = lengthSizeText.Width / 2+1;
- offset_X = Math.Max(offset_X, lengthSizeText.Width / 2)+1;
- offset_Y = Math.Max(offset_Y, lengthSizeText.Height)+1;
- //图标区域实际的大小,用于计算坐标转换
- curvewidth = retbit.Width - offset_X - curve_Right;
- curveheight = retbit.Height - offset_Y - curve_Top;
- axies
- //PointF pt_center = new PointF(offset_X, retbit.Height - offset_Y);
- //g.DrawLine(arrowPen, pt_center, new PointF(pt_center.X, 0));//y
- //g.DrawLine(arrowPen, pt_center, new PointF(retbit.Width, pt_center.Y));//x
- //
- ratio_X = curvewidth / ((ScaleX_Max - ScaleX_Min) );
- ratio_Y = curveheight / ((ScaleY_Max - ScaleY_Min)); //
- //
- for (int i = 0; i < x.Length; i++)
- {
- drawPts[i] = new PointF(x[i] * ratio_X - ScaleX_Min * ratio_X, y[i] * ratio_Y - ScaleY_Min * ratio_Y);
- drawPts[i] = new PointF(drawPts[i].X, -drawPts[i].Y);
- }
- //
- g.TranslateTransform(offset_X, retbit.Height - offset_Y);
- //drawing
- g.FillRectangle(new SolidBrush(backColor_Curve), new RectangleF(0, -curveheight, curvewidth, curveheight));
- //scales :x,y
- DrawScale(g);
- //curve
- g.DrawCurve(pen, drawPts);
- //symbol 画点
- DrawSymbol(g);
- }
- //
- private SizeF SetText(Graphics g,ref float offset_X,ref float offset_Y)
- {
- //因为刻度值坐标轴移动的长度
- offset_X = 0;
- offset_Y = 0;
- //
- float lengthText_X_Max = 0;
- float lengthText_Y_Max = 0;
- //坐标值
- text_X = new string[stepes_X];
- text_Y = new string[stepes_Y];
- for (int i = 0; i < text_X.Length; i++)
- {
- text_X[i] = ScaleX_Min + i * stepSize_X + "f";
- SizeF tmpSize = g.MeasureString(text_X[i], _textFont);
- offset_Y = Math.Max(offset_Y, tmpSize.Height);
- lengthText_X_Max = Math.Max(lengthText_X_Max, tmpSize.Width);
- }
- for (int i = 0; i < text_Y.Length; i++)
- {
- text_Y[i] = ScaleY_Min + i * stepSize_Y + "";
- SizeF tmpSize = g.MeasureString(text_Y[i], _textFont);
- offset_X = Math.Max(offset_X, tmpSize.Width);
- lengthText_Y_Max = Math.Max(lengthText_Y_Max, tmpSize.Height);
- }
- return new SizeF(lengthText_X_Max, lengthText_Y_Max);
- }
- //画曲线的点
- private void DrawSymbol(Graphics g)
- {
- Pen pen=new Pen(Color.Blue);
- float r = 2;
- for (int i = 0; i < drawPts.Length; i++)
- {
- PointF center = new PointF(drawPts[i].X - r, drawPts[i].Y - r);
- g.FillEllipse(new SolidBrush(backColor_Curve), center.X, center.Y, 2 * r, 2 * r);
- g.DrawEllipse(pen, center.X, center.Y, 2 * r, 2 * r);
- }
- }
- //画坐标轴
- private void DrawScale(Graphics g)
- {
- //x,y axis
- PointF pt_center = new PointF(0, 0);
- g.DrawLine(arrowPen, pt_center, new PointF(pt_center.X, -curveheight));//y
- g.DrawLine(arrowPen, pt_center, new PointF(curvewidth, pt_center.Y));//x
- //grid
- DrawGrid(g);
- //micro grid
- DrawMicroGrid(g);
- //draw text 刻度值
- DrawText(g);
- }
- private void DrawGrid(Graphics g)
- {
- Pen pen_Grid = new Pen(Color.Black);
- //x
- for (int i = 1; i < stepes_X; i++)
- {
- PointF pt1 = new PointF(i * stepSize_X * ratio_X, 0);
- PointF pt2 = new PointF(pt1.X, -curveheight);
- g.DrawLine(pen_Grid, pt1, pt2);
- }
- //y
- for (int i = 1; i < stepes_Y; i++)
- {
- PointF pt1 = new PointF(0, -i * stepSize_Y * ratio_Y);
- PointF pt2 = new PointF(curvewidth, pt1.Y);
- g.DrawLine(pen_Grid, pt1, pt2);
- }
- }
- private void DrawMicroGrid(Graphics g)
- {
- Pen pen_MGrid = new Pen(Color.Black);
- pen_MGrid.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
- //x
- for (int i = 0; i < stepes_X-1; i++)
- {
- PointF pt1 = new PointF((i+0.5f) * stepSize_X * ratio_X, 0);
- PointF pt2 = new PointF(pt1.X, -curveheight);
- g.DrawLine(pen_MGrid, pt1, pt2);
- }
- //y
- for (int i = 0; i < stepes_Y-1; i++)
- {
- PointF pt1 = new PointF(0, -(i + 0.5f) * stepSize_Y * ratio_Y);
- PointF pt2 = new PointF(curvewidth, pt1.Y);
- g.DrawLine(pen_MGrid, pt1, pt2);
- }
- }
- private void DrawText(Graphics g)
- {
- Pen pen_Grid = new Pen(Color.Black);
- //
- int no_x = 1, no_y = 1;
- float tmp=stepSize_X * ratio_X;
- while (tmp <= lengthSizeText.Width)
- {
- tmp = tmp * 2;
- no_x *= 2;
- }
- tmp = stepSize_Y * ratio_Y;
- while (tmp < lengthSizeText.Height)
- {
- tmp *= 2;
- no_y *= 2;
- }
- //x
- for (int i = 0; i < stepes_X/no_x+1; i++)
- {
- if (i * no_x >= stepes_X)
- continue;
- string text = text_X[i * no_x];
- SizeF textSize = g.MeasureString(text, _textFont);
- PointF pt1 = new PointF(i * no_x * stepSize_X * ratio_X - textSize.Width / 2, 5);
- g.DrawString(text, _textFont, Brushes.Black, pt1);
- }
- //y
- for (int i = 0; i < stepes_Y/no_y+1; i++)
- {
- if (i * no_y >= stepes_Y)
- return;
- string text = text_Y[i * no_y];
- SizeF textSize = g.MeasureString(text, _textFont);
- PointF pt1 = new PointF(-textSize.Width-5, -i * no_y * stepSize_Y * ratio_Y-textSize.Height/2);
- g.DrawString(text, _textFont, Brushes.Black, pt1);
- }
- }
- //
- protected double CalcStepSize(double range, double targetSteps)
- {
- // Calculate an initial guess at step size
- double tempStep = range / targetSteps;
- // Get the magnitude of the step size
- double mag = Math.Floor(Math.Log10(tempStep));
- double magPow = Math.Pow((double)10.0, mag);
- // Calculate most significant digit of the new step size
- double magMsd = ((int)(tempStep / magPow + .5));
- // promote the MSD to either 1, 2, or 5
- if (magMsd > 5.0)
- magMsd = 10.0;
- else if (magMsd > 2.0)
- magMsd = 5.0;
- else if (magMsd > 1.0)
- magMsd = 2.0;
- return magMsd * magPow;
- }
- //properties
- public Bitmap CurveImage
- {
- get { return retbit; }
- }
- }
调用代码是:
- //pts
- PointF[] pts = new PointF[]{
- //new PointF(-0.3f,-2.23f),
- new PointF(0.4f,3.191f),new PointF(0.5f,4.264f),new PointF(0.6f,5.525f),
- new PointF(0.7f,6.964f),new PointF(0.8f,8.436f),new PointF(0.9f,9.582f),
- new PointF(1.0f,10f),new PointF(1.1f,9.654f),new PointF(1.2f,8.878f),
- new PointF(1.3f,7.998f),new PointF(1.4f,7.179f),new PointF(1.5f,6.470f),
- new PointF(1.6f,5.871f)
- };
- Curve c = new Curve(pts);
- g.DrawImage(c.CurveImage, new Point(10, 10));
由于最近没有时间,只能先把代码贴了上去。这是原稿还没怎么整理。
大家提提意见。准备后续开发一个图表控件。^.^
有一点bug,需要将curve.cs中的DrawCurve方法替换一下:
- //
- public Bitmap DrawCurve(int width,int height)
- {
- retbit = new Bitmap(width, height);
- Graphics g = Graphics.FromImage(retbit);
- g.FillRectangle(Brushes.LightGray, new Rectangle(0, 0, retbit.Width, retbit.Height));
- arrowPen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
- arrowPen.CustomEndCap = new System.Drawing.Drawing2D.AdjustableArrowCap(arrowPen.Width * 3, arrowPen.Width * 5);
- Pen pen = new Pen(Color.Red);
- //计算y的起始刻度值
- int tmpInt;
- stepSize_Y = (float)CalcStepSize(range_Y, 10);
- //y 最小刻度值
- tmpInt = Convert.ToInt32(Y_Min / stepSize_Y);
- if (tmpInt < 0)
- tmpInt = tmpInt - 1;
- ScaleY_Min = tmpInt * stepSize_Y;
- //y最大刻度值
- if (Convert.ToInt32( Convert.ToInt32(Y_Max / stepSize_Y) - Y_Max / stepSize_Y)==0 )
- tmpInt = Convert.ToInt32(Y_Max / stepSize_Y);
- else
- tmpInt = Convert.ToInt32(Y_Max / stepSize_Y + 1);
- ScaleY_Max = tmpInt * stepSize_Y;
- stepes_Y = Convert.ToInt32((ScaleY_Max - ScaleY_Min) / stepSize_Y)+1;
- //计算x的起始刻度值
- stepSize_X = (float)CalcStepSize(range_X, 10);
- //x最小刻度
- tmpInt = Convert.ToInt32(X_Min / stepSize_X);
- if (tmpInt < 0)
- tmpInt = tmpInt - 1;
- ScaleX_Min = tmpInt * stepSize_X;
- //x最大刻度
- if (Convert.ToInt32(Convert.ToInt32(X_Max / stepSize_X) - X_Max/stepSize_X)==0)
- tmpInt = Convert.ToInt32(X_Max / stepSize_X);
- else tmpInt = Convert.ToInt32(X_Max / stepSize_X + 1);
- ScaleX_Max = tmpInt * stepSize_X;
- stepes_X = Convert.ToInt32((ScaleX_Max - ScaleX_Min) / stepSize_X)+1;
- //
- float offset_X = 0;
- float offset_Y = 0;
- lengthSizeText = SetText(g,ref offset_X,ref offset_Y);
- //图标距离顶部的高度,
- //预留的坐标轴箭头空间
- float curve_Top = lengthSizeText.Height/2;
- float curve_Right = lengthSizeText.Width / 2+1;
- offset_X = Math.Max(offset_X, lengthSizeText.Width / 2)+1;
- offset_Y = Math.Max(offset_Y, lengthSizeText.Height)+1;
- //图标区域实际的大小,用于计算坐标转换
- curvewidth = retbit.Width - offset_X - curve_Right;
- curveheight = retbit.Height - offset_Y - curve_Top;
- //
- ratio_X = curvewidth / ((ScaleX_Max - ScaleX_Min) );
- ratio_Y = curveheight / ((ScaleY_Max - ScaleY_Min)); //
- //
- for (int i = 0; i < x.Length; i++)
- {
- drawPts[i] = new PointF(x[i] * ratio_X - ScaleX_Min * ratio_X, y[i] * ratio_Y - ScaleY_Min * ratio_Y);
- drawPts[i] = new PointF(drawPts[i].X, -drawPts[i].Y);
- }
- //
- g.TranslateTransform(offset_X, retbit.Height - offset_Y);
- //drawing
- g.FillRectangle(new SolidBrush(backColor_Curve), new RectangleF(0, -curveheight, curvewidth, curveheight));
- //scales :x,y
- DrawScale(g);
- //curve
- g.DrawCurve(pen, drawPts);
- //symbol 画点
- DrawSymbol(g);
- return retbit;
- }