有没有办法在GDI +中获得比使用DrawCircle更精确的圆圈?

时间:2015-03-05 15:09:16

标签: c# graphics gdi+ floating-point-precision

我正在努力处理一个情况,我想用GDI +绘制一个圆圈并在其上绘制一些点(绘制为较小的圆圈),但圆圈似乎是非圆形的。通过实现缩放和零点移动,我可以放大点和圆,并找到不完全位于圆上的点。

当我添加'离散'如果使用了足够的段,则使用线段绘制的圆圈非常适合点。由于数学是相同的,我认为我的代码中的舍入误差不能成为圆偏差的原因(尽管可能在DrawEllipse的实现中)。

事实上,在45/135/225/315度的偏差最大。

我制作了一个小项目,用稍微不同的设置再现效果:我绘制了多个圆圈,它们的原点位于另一个圆上,其中心位于表格中心,半径相同。如果一切顺利,所有圈子都应该触摸表格的中心。但是像100和39这样的大半径,它们不再通过中心了,但是它可以通过大约15像素的距离来错过它。

要重现这种情况,请创建一个新的c#项目,放在form1 button1上,并从中调用Draw()函数:

private void Draw()
{
    Graphics g = this.CreateGraphics();
    g.Clear(this.BackColor );

    double hw = this.Width / 2;
    double hh = this.Height / 2;
    double scale = 100000;

    double R = 1;

    for (int i = 0; i < 12; i++)
    {
        double angle = i * 30;

        double cx = R * Math.Cos(angle * Math.PI / 180);
        double cy = R * Math.Sin(angle * Math.PI / 180);

        g.DrawEllipse(new Pen(Color.Blue, 1f), (float)(hw - scale * (cx + R)), (float)(hh + scale * (cy - R)), (float)(2 * R * scale), (float)(2 * R * scale));

        g.DrawLine(Pens.Black, new Point(0, (int)hh), new Point(this.Width, (int)hh));
        g.DrawLine(Pens.Black, new Point((int)hw, 0), new Point((int)hw, this.Height));

        double r = 3;

        g.DrawEllipse(new Pen(Color.Green, 1f), (float)(hw - r), (float)(hh - r), (float)(2 * r), (float)(2 * r));

        //Pen magpen = new Pen(Color.Magenta, 1);
        //double n = 360d / 1000;
        //for (double j = 0; j < 360; j += n)
        //{
        //    double p1x = R * Math.Cos(j * Math.PI / 180) + cx;
        //    double p1y = R * Math.Sin(j * Math.PI / 180) + cy;
        //    double p2x = R * Math.Cos((j + n) * Math.PI / 180) + cx;
        //    double p2y = R * Math.Sin((j + n) * Math.PI / 180) + cy;

        //    g.DrawLine(magpen, (float)(hw - scale * p1x), (float)(hh + scale * p1y), (float)(hw - scale * p2x), (float)(hh + scale * p2y));
        //}
    }            
}

使用100到100&000; 000之间的变量刻度来查看效果:圆圈不再触碰原点,但在它周围摆动。如果您取消注释您可以看到的注释行,那么洋红色的“离散”线条就会消失。圈子表现得更好。

使用&#39;离散&#39;圆圈和圆弧是性能杀手,我正在寻找一种用GDI +绘制更好圆圈的方法。

任何想法,建议?

2 个答案:

答案 0 :(得分:1)

GDI+(至少在其原生版本中)不绘制椭圆:它使用三次贝塞尔近似,如 herehere 所述。

即代替

   c = cos(angle);
   s = sin(angle);
   x = c * radius;
   y = s * radius;

或者,对于椭圆(a & b 以某种顺序作为长半轴和半短轴)

   x = c * a;
   y = s * b;

使用

   K = (sqrt(2) - 1) * 4.0 / 3;
   t = angle * 0.5 / pi;
   u = 1 - t;
   c = (u * u * u) * 1 + (3 * u * u * t) * 1 + (3 * u * t * t) * K + (t * t * t) * 0;
   s = (u * u * u) * 0 + (3 * u * u * t) * K + (3 * u * t * t) * 1 + (t * t * t) * 1;

(对于 [0, pi/2] 范围内的角度:其他象限可以通过对称计算)。

答案 1 :(得分:0)

g.DrawArc(new Pen(Color.Blue, 1f), (float)((hw) - R * scale), -(float)((2 * R * scale) - hh), (float)(2 * R * scale), (float)(2 * R * scale), 0, 360);

我用这种方法得到了一些精确的结果。但这只是一个样本。