日期:2014-05-18  浏览次数:21113 次

「玩一玩」GDI+绘制极坐标图(Polar Diagram)
各位,又是我。
首先,本文有点长,。最近我的文风开始往废话连篇发展了。
其次,介绍本文主要内容。

这是一个简单的GDI+的例子。讲的是怎么从无到有绘制一个极坐标系,以及在此基础上绘制数据图。按照类似的思路,你可以画出直角坐标系、对数直角系、外太空银河系……

本文比较浅显,觉得没有帮助者请按组合键:Alt+F4,走好。

欢迎大家指教,欢迎改造,然后把代码和图贴在这里。大家一起看看能把自己的创造力激发到什么程度。

给出源代码:点击下载

你可以通过本文学到怎么用非数学的方法解决数学问题,以此类推,就算碰到不懂的东西,也可以用自己懂的东西来代替。编程序,尤其是界面编程,只需要「看起来一样」,更敬业点就「用起来一样」。至于你要不要用什么jjyy的技术,的技巧,的手段,都是浮云。

不多说了。我们要做的东西最后效果是这样的。



图中曲线是一个天线方向图,非常适合在极坐标下描绘。
文中是直接在窗体上绘制,你完全可以自行封装到控件里,这样用起来更加方便。

(正文开始)
写在前面的话
做事情,一切以目标为出发点,倒着找过去,看有哪些方法技术资源,具体的方法技术手段都是次要的,只要能达到目的。
我不会多线程,如果你需要让它运行在单独的线程上,还请自己改造。(似乎还真有这样ocd的人吧,哈哈)

目标设定
(下面是例子,不针对任何人物、事件、团体、星球)
boss接到了一单生意,是帮某山寨厂做一个山寨手机天线的信号测试系统。其中,我分到的部分是做天线方向图的显示界面模块。其实我懂个p的天线、方向图之类的啊,于是boss告诉我,并强调:我不管你怎么做,总之要「看起来」像这样。



ok,不管会不会,山寨是本行,拿着原版开始分析。

分析坐标系
说实话,数学那套玩意老早就还给老师了,现在要让我玩坐标系这样高深的东西。得亏哥们还有点印象,这样圆不拉叽的图,一般用极坐标来画是比较方便的。连上Wikipedia复习一下:极坐标是一个二维坐标系统。该坐标系统中的点由一个夹角和一段相对中心点——极点(相当于我们较为熟知的直角坐标系中的原点)的距离来表示。



嗯,很好,乱七八糟的,看不太懂啊,怎么办。不管了,把这东西先放一遍,还是用山寨的方法解决。把boss给的那张图拿来分析下,其实就是很多同心圆,和过圆心的辐条(借用自行车术语,虽然不知道正确的名字,就这么叫了吧)。

那么我只需要画出同心圆,再画辐条,就ok了吧。画同心圆怎么画呢?嗯,我可以这样,从外面的大圆开始,用DrawEllipse()画一个圆,然后收缩下半径,再画一个,如此这般……好了,有想法就行动,管他是nb方法还是sb方法,一直坐那zb,最后被炒了那才sb。

画出同心圆的方法。

C# code
    // 画圆
    private void drawCircles(Graphics g, Rectangle rect)
    {
        // 圆的直径等于绘图区域最短边
        float diameter = Math.Min(rect.Width, rect.Height);
        // 半径
        float radius = diameter / 2;
        // 计算圆心(其实就是绘图区域矩形中心)
        PointF center = new PointF(
            rect.X + rect.Width / 2,
            rect.Y + rect.Height / 2
            );

        // 画几个圆,先试试5个
        int count = 5;
        float diameterStep = diameter / count;
        float radiusStep = radius / count;

        // 生成圆的范围
        RectangleF cirleRect = new RectangleF();
        cirleRect.X = center.X - radius;
        cirleRect.Y = center.Y - radius;
        cirleRect.Width = cirleRect.Height = diameter;

        // 画同心圆
        for (int i = 0; i < count; i++)
        {
            g.DrawEllipse(Pens.Gray, cirleRect);

            cirleRect.X += radiusStep;
            cirleRect.Y += radiusStep;
            cirleRect.Width -= diameterStep;
            cirleRect.Height -= diameterStep;
        }
    }


把这段代码添加到Paint事件里,看看效果如何。



Good,效果还凑合,好像有点锯齿哦,那我就把抗锯齿打开,顺手把文字抗锯齿也打开。

C# code
    e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;    // 图形抗锯齿
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; // 文字抗锯齿


接下来就要画辐条了。那个线可不能就像图里一个十字叉就完事了的,肯定要能自己设n条。想当初就是曾经思维简单了没有考虑到这种变数,被客户和boss烦得天昏地暗。再也不会上当了。

辐条怎么画呢,思考下,在草稿纸上画画先。

(以下都是中学数学,本人上了大学以后数学从没及格过)



从少到多看看辐条的规律。啊,原来是这样啊,我不一定非要把辐条看成穿过圆心的,我可以看成从圆心发出的n个射线,把圆切成了n个扇面,每个角度就是360°÷n。这样那就好办了,刚才我画圆的时候已经算出来圆心位置了,只要再算出射线终点的坐标,就可以用DrawLine()画线了。但是,射线终点又要怎么算呢,我可是要画到GDI+里哦。



用黑色的笔画出圆,红色的画出GDI+坐标系,那么就可以算出来终点在GDI+下的坐标。圆心(x0,y0)和r半径刚才我已经算出来了,θ就是360/n。现在所有参数都确定了,只要把圆心、半径这几个我需要使用的变量从画圆的方法里拿出来大家用,我就可以开始写画辐条的方法了。

C# code
    // 提出来公用
    float diameter, radius;
    PointF center;
    // 画圆
    private void drawCircles(Graphics g, Rectangle rect)
    {
        // (略)
    }

    // 辐射线
    private void drawCrosshair(Graphics g)
    {
        int count = 8;
        if (count > 0)
        {
            // 计算角度