ZedGraph源码学习(二)

昨天大致介绍下了PaneBase,只是介绍了界面伸缩的一些实现,和我开始说的Pane里包含了所有要绘制的元素不符,其实这些部分的实现,全交给了它的子类GraphPane来实现,下面来让我们看看这个类的一些实现。

这里我先说下在GraphPane里的坐标轴,X轴的情况不复杂,分别为下

轴(xAxis)与上面的x轴(x2Axis),Y轴则有些不同,它分为左边的Y轴集合(yAxisList),右边的Y轴集合(y2AxisList).还有一个比较重要的属性就是昨天所说的所需要画的所有的元素CurveItem集合CurveList。

我们先看下如何轴上的数据是如何初始化的。

在AxisChange()这个函数里,首先会调用GraphPane里的CurveList的GetRange()方法,我们先看下这个方法的基本实现。

		public void GetRange( bool bIgnoreInitial, bool isBoundedRanges, GraphPane pane )
		{
			double	tXMinVal,
						tXMaxVal,
						tYMinVal,
						tYMaxVal;

			InitScale( pane.XAxis.Scale, isBoundedRanges );
			InitScale( pane.X2Axis.Scale, isBoundedRanges );

			foreach ( YAxis axis in pane.YAxisList )
				InitScale( axis.Scale, isBoundedRanges );

			foreach ( Y2Axis axis in pane.Y2AxisList )
				InitScale( axis.Scale, isBoundedRanges );

			maxPts = 1;
			
			// Loop over each curve in the collection and examine the data ranges
			foreach ( CurveItem curve in this )
			{
				if ( curve.IsVisible )
				{
					// For stacked types, use the GetStackRange() method which accounts for accumulated values
					// rather than simple curve values.
					if ( ( ( curve is BarItem ) && ( pane._barSettings.Type == BarType.Stack ||
							pane._barSettings.Type == BarType.PercentStack ) ) ||
						( ( curve is LineItem ) && pane.LineType == LineType.Stack ) )
					{
						GetStackRange( pane, curve, out tXMinVal, out tYMinVal,
										out tXMaxVal, out tYMaxVal );
					}
					else
					{
						// Call the GetRange() member function for the current
						// curve to get the min and max values
						curve.GetRange( out tXMinVal, out tXMaxVal,
										out tYMinVal, out tYMaxVal, bIgnoreInitial, true, pane );
					}

					// isYOrd is true if the Y axis is an ordinal type
					Scale yScale = curve.GetYAxis( pane ).Scale;

					Scale xScale = curve.GetXAxis( pane ).Scale;
					bool isYOrd = yScale.IsAnyOrdinal;
					// isXOrd is true if the X axis is an ordinal type
					bool isXOrd = xScale.IsAnyOrdinal;

					// For ordinal Axes, the data range is just 1 to Npts
					if ( isYOrd && !curve.IsOverrideOrdinal )
					{
						tYMinVal = 1.0;
						tYMaxVal = curve.NPts;
					}
					if ( isXOrd && !curve.IsOverrideOrdinal )
					{
						tXMinVal = 1.0;
						tXMaxVal = curve.NPts;
					}

					// Bar types always include the Y=0 value
					if ( curve.IsBar )
					{
						if ( pane._barSettings.Base == BarBase.X ||
								pane._barSettings.Base == BarBase.X2 )
						{
							// Only force z=0 for BarItems, not HiLowBarItems
							if ( ! (curve is HiLowBarItem)  )
							{
								if ( tYMinVal > 0 )
									tYMinVal = 0;
								else if ( tYMaxVal < 0 )
									tYMaxVal = 0;
							}

							// for non-ordinal axes, expand the data range slightly for bar charts to
							// account for the fact that the bar clusters have a width
							if ( !isXOrd )
							{
								tXMinVal -= pane._barSettings._clusterScaleWidth / 2.0;
								tXMaxVal += pane._barSettings._clusterScaleWidth / 2.0;
							}
						}
						else
						{
							// Only force z=0 for BarItems, not HiLowBarItems
							if ( !( curve is HiLowBarItem ) )
							{
								if ( tXMinVal > 0 )
									tXMinVal = 0;
								else if ( tXMaxVal < 0 )
									tXMaxVal = 0;
							}

							// for non-ordinal axes, expand the data range slightly for bar charts to
							// account for the fact that the bar clusters have a width
							if ( !isYOrd )
							{
								tYMinVal -= pane._barSettings._clusterScaleWidth / 2.0;
								tYMaxVal += pane._barSettings._clusterScaleWidth / 2.0;
							}
						}
					}

					// determine which curve has the maximum number of points
					if ( curve.NPts > maxPts )
						maxPts = curve.NPts;

					// If the min and/or max values from the current curve
					// are the absolute min and/or max, then save the values
					// Also, differentiate between Y and Y2 values

					if ( tYMinVal < yScale._rangeMin )
						yScale._rangeMin = tYMinVal;
					if ( tYMaxVal > yScale._rangeMax )
						yScale._rangeMax = tYMaxVal;


					if ( tXMinVal < xScale._rangeMin )
						xScale._rangeMin = tXMinVal;
					if ( tXMaxVal > xScale._rangeMax )
						xScale._rangeMax = tXMaxVal;
				}
			}

			pane.XAxis.Scale.SetRange( pane, pane.XAxis );
			pane.X2Axis.Scale.SetRange( pane, pane.X2Axis );

			foreach ( YAxis axis in pane.YAxisList )
				axis.Scale.SetRange( pane, axis );
			foreach ( Y2Axis axis in pane.Y2AxisList )
				axis.Scale.SetRange( pane, axis );
		}

在这里我们可以看到,这个方法是先初始化所有的X与Y轴上的一些基本数据,如最小值,最大值,步长等数据。然后遍历所有CurveList的所有数据,根据我们要画的不同的元素分别得到X与Y轴的最小值和最大值。

然后判断当前Curve所对应的轴的的X轴与Y轴是否有序。这个有序我大致说下意思,大家可以看下有些坐标轴,它的轴上不是一些数字,而是一些标示,一般是些描述,那么这种我们称之有序,而那种轴上是一些数字有规律排列,我们则不称有序。这个大家要查看具体的可以查找enum类AxisType的各个属性值的具体描述,这里我不多做说明。

X轴和Y轴通过它们对就的Curve得到它们对应的要显示的最大值和最小值后,这段逻辑我们也可以看到,假设二个Curve对应的是同一个X与Y轴,那么最小值和最大值分别取二个Curve里的最小值和最大值,所以如果多个Curve的话,并且它们同X轴或是同Y轴的最大值和最小值差值很大的话,请考虑分开它们的坐标轴是一个好的选择。确定了对应轴要显示的最大与最小数据(RangeMin与RangeMax),在这里我有个疑问,这里只是确定轴所要显示的最大值的最小值,但是我们看到的轴图,都会多显示一些数据并且还有相应的的步长都没确定下来。

然后我们回到AxisChange()里,可以看到会调用一个PickScale()的方法,这里前面一部分代码会遍历Pane上所有的轴然后调用每一个轴上的scale上的PickScale()方法,跟进去看发现还是很明白的。我们先假设是最普通的笛卡尔坐标轴,就是LineraScale类型,会先调用父类Scale类里的PickScale()方法,在这方法里我们能看到分别有二个参数minGrace与maxGrace,通过查看这二个属性的描述,我们明白了这二个参数的意思,如果我们要显示的最小值RangeMin为5,最大值RangeMax=40,则显示的Range=RangeMax-RangeMin=35那么我们在轴上显示最小值Min应该是是RangeMin-RangeMin*Range=1.5,同理Max应该是43.5。我们清楚了父类Scale的这个方法就是确定坐标轴的min与max值。

然后我们回到本身的PickScale()方法里,这里下走,可以看到如果有句代码min/(max-min)<0.25,则min=0,这里的意思就是如果min相对于整个要显示的范围太少,则把最小值设为零,我们看上面,1.5/(43.5-1.5)肯定没有0.25大,于是最小值min则为零了.好了,min与max显示完善了,那么步长了,别急,在下面的_majorStep = CalcStepSize( _max - _min, targetSteps );在CalcStepSize这个函数里,如下代码所示。

		protected static 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;
		}

这代码主要是为了选择一些能除2,5,10的,大致结果是Range11-17时选择2,18-38时为5,39-104为10,这个范围的10的N次倍的结果也是N次,如200就是50,2就是0.5,2000就是50.设置好主步长和,然后就是副步长。里面还有些函数如CalcMaxLabels这些主要是确定轴上最多能放多少个标识点,这个如果后面讲到scale我会具体说。

回到AxisChange()里,然后会对要生成的chart图通过CalcChartRect()来计算绘图的方位,在这个函数里,会除去对应标题,轴上数据显示所要占的空间,点到只是绘制的可用面积与方位。到这后GraphPane初始化它上面的各个对象的数据,然后这些对象就能通过这些数据来绘制它们自己而显示成我们所看到的各个精彩的图表控件。后面我会讲各个元素的绘制过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ZedGraph控件属性设置 ZedGraph是一个非常优秀的开源的作图控件 ZedGraph来源:http://sourceforge.net/project/showfiles.php?group_id=114675 ZedGraph相关例子资源:http://zedgraph.org/wiki/index.php?title=Sample_Graphs 1、在vs中使用ZedGraph 2、一些基本概念 几个注意点: 图片的保存路径设置:RenderedImagePath属性中设置,程序对该文件夹应该是有写和修改权限的 图片的输出格式:OutputFormat属性中设置,Png的推荐,比较清晰。 Chart ChartBorder 图表区域的边框设置 ChartFill 图表区域的背景填充 Legend 图表的注释标签显示设置项目,一组数据对应一种颜色的注释 IsHStack 当有多个显示项的时候设置Y轴数据是叠加的还是分开的 Xaxis 图表区域的X轴相关信息设置 AxisColor 坐标轴颜色 Cross 坐标的原点,可以设置坐标的偏移程度 CrossAuto 原点自动设置:True的话Cross的设置就无效了。 FontSpec X轴标题字体相关信息 Angle X轴标题字体显示时候的角度,0为水平 90为垂直 Fill X轴标题字体填充信息 ColorOpacity 透明度 IsScaled 设置X轴标题字体显示大小是否根据图的比例放大缩小 RangeMax 填充时候的最大倾斜度(有过渡色,没试过) RangeMin 填充时候的最小倾斜度(有过渡色,没试过) StringAlignment X轴标题字体排列(不清楚,没试过) IsOmitMag 是否显示指数幂(10次方,没试过,似乎与IsUseTenPower有关系) IsPreventLabelOverlap 坐标值显示是否允许重叠,如果False的话,控件会根据坐标值长度自动消除部分坐标值的显示状态 IsShowTitle X轴标题是否显示 IsTicsBetweenLabels 两个坐标值之间是否自动显示分隔标志 IsUseTenPower 是否使用10次幂指数 IsVisible 是否显示X轴 IsZeroLine 当数据为0时候是否显示(在饼状图显示的时候有用) MajorGrid 大跨度的X轴表格虚线线显示信息 DashOff 虚线中孔间距 DashOn 虚线单位长度 MajorTic 大跨度的X轴刻度信息 IsInside 在Chart内部是否显示 IsOutSide 在Chart外部是否显示 IsOpposite
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值