[C#]绘制函数图像.可拖动,可缩放,可调整精度
欸嘿,这就是程序图了,通过⿏标拖拽可以移动,⿏标滚轮可以缩放,右下⾓还可以选择要绘制的函数.项⽬仓库链接在⽂章末尾
基本原理:
Graphics绘图,不⽤我说了吧?如果你不是很懂,留⾔,我会专门写⼀篇⽂章来介绍Graphics.
带⼊求值,没啥难的.线是⼀个个点连起来的,也就是:
然后,标尺,也是⼀个个线呗,那个数字的话,就是这个:
填充⼩三⾓的话,就是这个:
关于优化:
⾸先是计算问题,保证仅仅计算需要显⽰的区域,区域外的坐标不予以计算,以节省资源.
然后是闪屏问题,使⽤BufferedGraphics,既能解决闪屏问题,⼜不会像Bitmap缓冲那样闪屏.
关于绘图闪屏问题,⽹上有很多解决⽅案,最核⼼的,⽆⾮是双缓冲,也就是先将图绘制到缓冲区,再将缓冲区的内容绘制到屏幕上.
实现双缓冲有两种⽅式,⼀就是通过创建⼀个Bitmap,将图画到Bitmap上,然后画完之后,再将Bitmap画到屏幕上
(age()),缺点是会造成撕裂问题.
第⼆种⽅式是BufferedGraphics,这是⼀个⽐Bitmap更好⽤的东西,不会造成撕裂.
封装⼀下:
using;
using;
using;
using;
namespace
{
publicstaticclassFuncDraw
{
///
///根据数字坐标获取像素位置
///
///
///
///
///
///
///
publicstaticPointGetPointFromCoords(doublexCoord,doubleyCoord,intxOfft,intyOfft,doublescale)
{
returnnewPoint((int)(xCoord*scale+xOfft),(int)(-yCoord*scale+yOfft));
}
///
///
///
///
///
///
///
publicstaticPointGetPointFromCoords(PointFcoords,Pointofft,doublescale)
{
returnnewPoint((int)(coords.X*scale+offt.X),(int)(-coords.Y*scale+offt.Y));
}
///
///根据像素位置获取数字坐标
///
///
///
///
///
///
///
///
publicstaticvoidGetCoordsFromPoint(intx,inty,intxOfft,intyOfft,doublescale,outdoublexCoord,outdoubleyCoord)
{
xCoord=(x-xOfft)/scale;
yCoord=-((y-yOfft)/scale);
return;
}
///
///根据像素长度返回数字
///
///
///
///
publicstaticdoubleGetNumberFromPixel(intlength,doublescale)
{
returnlength/scale;
}
///
///根据数字来获取它距离原点的像素长度
///
///
///
///
publicstaticdoubleGetPixelFromNumber(intnumber,doublescale)
{
returnnumber*scale;
}
///
///画函数图像
///
///
///
///
///
///
///
///
///
publicstaticvoidDrawFunc(Func
ntyOfft,doublescale)
{
double[]nums=y();
Point[]coords=newPoint[];
intdrawAreaLeft=drawArea.X,
drawAreaRight=drawAreaLeft+,
drawAreaTop=drawArea.Y,
drawAreaBottom=drawAreaTop+;
doubley;
for(inti=0,len=;i
拆箱,所以这样效率⾼⼀些
{
y=(nums[i]);
coords[i]=GetPointFromCoords(nums[i],y,xOfft,yOfft,scale);
}
boolpoint1xIn1,
point1xIn2,
point1yIn1,
point1yIn2,
point2xIn1,
point2xIn2,
point2yIn1,
point2yIn2;
for(inti=1,len=;i
{
Pointpoint1=coords[i-1];
Pointpoint2=coords[i];
point1xIn1=point1.X>=drawAreaLeft;
point1xIn2=point1.X<=drawAreaRight;
point1yIn1=point1.Y>=drawAreaTop;
point1yIn2=point1.Y<=drawAreaBottom;
point2xIn1=point2.X>=drawAreaLeft;
point2xIn2=point2.X<=drawAreaRight;
point2yIn1=point2.Y>=drawAreaTop;
point2yIn2=point2.Y<=drawAreaBottom;
if((!(point1xIn1&&point2xIn1))||(!(point1xIn2&&point2xIn2))||(!(point1yIn1&&point2yIn1))||(!(point1yIn2&&point2yIn2)))
continue;
if(!point1xIn1)
point1=newPoint(drawAreaLeft,(int)(point1.Y+(point2.Y-point1.Y)*((double)drawAreaLeft-point1.X)/(point2.X-point1.X)));//这⾥是
有⼀些奇妙优化的,不要删去.否则将导致某些函数⽆法绘制.
elif(!point2xIn1)
point2=newPoint(drawAreaLeft,(int)(point2.Y+(point1.Y-point2.Y)*((double)drawAreaLeft-point2.X)/(point1.X-point2.X)));
if(!point1xIn2)
point1=newPoint(drawAreaRight,(int)(point2.Y+(point1.Y-point2.Y)*((double)drawAreaRight-point2.X)/(point1.X-point2.X)));
elif(!point2xIn2)
point2=newPoint(drawAreaRight,(int)(point1.Y+(point2.Y-point1.Y)*((double)drawAreaRight-point1.X)/(point2.X-point1.X)));
if(!point1yIn1)
point1=newPoint((int)(point1.X+(point2.X-point1.X)*((double)drawAreaTop-point1.Y)/(point2.Y-point1.Y)),drawAreaTop);
elif(!point2yIn1)
point2=newPoint((int)(point2.X+(point1.X-point2.X)*((double)drawAreaTop-point2.Y)/(point1.Y-point2.Y)),drawAreaTop);
if(!point1yIn2)
point1=newPoint((int)(point2.X+(point1.X-point2.X)*((double)drawAreaBottom-point2.Y)/(point1.Y-point2.Y)),drawAreaBottom);
elif(!point2yIn2)
point2=newPoint((int)(point1.X+(point2.X-point1.X)*((double)drawAreaBottom-point1.Y)/(point2.Y-point1.Y)),drawAreaBottom);
ne(pen,point1,point2);
}
}
///
///画⼀个坐标轴
///
///
///
///
///
///
///
///
///
///
///
///
///
publicstaticvoidDrawShaft(IEnumerable
RectangledrawArea,intxOfft,intyOfft,intbarLength,doublescale,booltext)
{
Action
if(text)
drawText=(point,num)=>ring(ng("F2"),font,brush,point);//使⽤委托,那么接下来就不需要进⾏多余的对text的
判断,⽽直接Invoke就⼻亍
el
drawText=(point,num)=>{};
Point
yTop=newPoint(xOfft,drawArea.Y),
yBottom=newPoint(xOfft,drawArea.Y+),//确认轴的位置
xLeft=newPoint(drawArea.X,yOfft),
xRight=newPoint(drawArea.X+,yOfft);
ne(pen,yTop,yBottom);//画轴
ne(pen,xLeft,xRight);
inttriangleHeight=(int)((/3)*barLength);//坐标轴末端⼩三⾓的⾼度
Point[]
triangle1=newPoint[]{yTop,newPoint(yTop.X-barLength,yTop.Y+triangleHeight),newPoint(yTop.X+barLength,yTop.Y+triangleHeight)
},
triangle2=newPoint[]{xRight,newPoint(xRight.X-triangleHeight,xRight.Y+barLength),newPoint(xRight.X-triangleHeight,xRight.Y-barLe
ngth)};
lygon(brush,triangle1);//画三⾓
lygon(brush,triangle2);
foreach(doublexinxNumbers)
{
PointnumBa=GetPointFromCoords(x,0,xOfft,yOfft,scale);
PointnumEnd=newPoint(numBa.X,numBa.Y-barLength);
ne(pen,numBa,numEnd);//画x轴数
drawText(numBa,x);
drawText(numBa,x);
}
foreach(doubleyinyNumbers)
{
PointnumBa=GetPointFromCoords(0,y,xOfft,yOfft,scale);
PointnumEnd=newPoint(numBa.X+barLength,numBa.Y);
ne(pen,numBa,numEnd);//画y轴数
drawText(numBa,y);
}
}
}
}
注意事项:
Graphics在绘制某些超级远的东西时,例如这个坐标时(2147483647,2147483647),总之就是⾮常⼤,那可能会不能绘制,报
Overflow异常(已解决)
还有⼀些要⽤的内容:
CSDN⽂章:
CSDN⽂章:
更新记录:
最新版本已经⽀持⾃定义函数表达式
项⽬:
项⽬仓库:
本文发布于:2023-03-08 01:15:11,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/zhishi/a/1678209312130111.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:xleft.doc
本文 PDF 下载地址:xleft.pdf
留言与评论(共有 0 条评论) |