c++ 布雷森汉姆的线算法绘制弧形和旋转

c++ Bresenham's line algorithm draw arc and rotate

本文关键字:绘制 旋转 算法 布雷森 c++      更新时间:2023-10-16

我正在寻找用布雷斯纳姆的直线算法制作弧的方法。这个算法画完美的圆,但如果我需要画弧(从0到Pi),并旋转它30度(例如)?

void DrawCircle(HDC hdc,int x0, int y0, int radius) 
{
        int x = 0;
        int y = radius;
        int delta = 2 - 2 * radius;
        int error = 0;
        while(y >= 0) {
                //SetPixel(hdc,x0 + x, y0 + y,pencol);
                SetPixel(hdc,x0 + x, y0 - y,pencol);
                //SetPixel(hdc,x0 - x, y0 + y,pencol);
                SetPixel(hdc,x0 - x, y0 - y,pencol);
                error = 2 * (delta + y) - 1;
                if(delta < 0 && error <= 0) {
                        ++x;
                        delta += 2 * x + 1;
                        continue;
                }
                error = 2 * (delta - x) - 1;
                if(delta > 0 && error > 0) {
                        --y;
                        delta += 1 - 2 * y;
                        continue;
                }
                ++x;
                delta += 2 * (x - y);
                --y;
        }
}

要得到1/2个圆(到pi),只调用SetPixel例程之一。要使圆弧旋转30度需要用到一些三角函数。你可以让上面的循环运行,直到你的x/y比率等于tan(30度),然后开始实际绘图,直到你的比率达到你想要停止的值。不是最有效的方法,但它会起作用。为了得到更好的结果,您需要预先计算开始的4个变量值。您可以从上面的运行中获取值,并将它们作为起始值插入,这将非常有效。

你是从Michael Abrash的黑皮书里得到上面的算法的吗?如果没有,我将谷歌作为快速圆/弧绘制的第二个参考点。

好吧,唉,省略的章节没有包括在那里。这是我在网上发现的一些声称来自Abrash的东西:


/* One of Abrash's ellipse algorithms  */
void draw_ellipse(int x, int y, int a, int b, int color)
{
    int wx, wy;
    int thresh;
    int asq = a * a;
    int bsq = b * b;
    int xa, ya;
    draw_pixel(x, y+b, color);
    draw_pixel(x, y-b, color);
    wx = 0;
    wy = b;
    xa = 0;
    ya = asq * 2 * b;
    thresh = asq / 4 - asq * b;
    for (;;) {
        thresh += xa + bsq;
        if (thresh >= 0) {
            ya -= asq * 2;
            thresh -= ya;
            wy--;
        }
        xa += bsq * 2;
        wx++;
        if (xa >= ya)
          break;

        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }
    draw_pixel(x+a, y, color);
    draw_pixel(x-a, y, color);
    wx = a;
    wy = 0;
    xa = bsq * 2 * a;
    ya = 0;
    thresh = bsq / 4 - bsq * a;
    for (;;) {
        thresh += ya + asq;
        if (thresh >= 0) {
            xa -= bsq * 2;
            thresh = thresh - xa;
            wx--;
        }
        ya += asq * 2;
        wy++;
        if (ya > xa)
          break;
        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }
}

意思是每次画1/8的圆x4,然后翻出剩下的1/8。但还是没有直接回答你的问题。正在努力……

同样,上面的代码应该可以工作,您只需要仔细控制开始和结束条件。y>= 0需要成为完成"弧"长度时的y值,并且需要计算起始值作为弧的起始值。

这将不是一个直接的任务,因为事情是这样的。使用浮点例程可能更容易。数学运算要简单得多,处理器也倾向于比设计这些整数例程时更好地处理它们。

如果你不需要确定的Bresenham,有一个快速的步骤方法介绍在这篇文章,你可以设置中心点,起点和圆弧角。它不需要停止准则,因为它已经包含在算法中(按圆角)。使它快速的是切向和径向运动因子的预计算,实际的循环没有三角函数调用,只有乘法、加法和减法。

我想有三种方法:
A)渐进式像布里森汉姆
B)像这样细分方法
C)步骤(或段)方法

我将举一个缓慢的步进方法的例子(如果速度很重要,不要使用这个):

// I know the question is tagged c++, but the idea comes clear in javascript
var start_angle = 0.5, end_angle = 1.1, r = 30;
for(var i = start_angle; i < end_angle; i = i + 0.05)
{
  drawpixel(x: 50 + Math.cos(i) * r, y: 100 + Math.sin(i) * r); // center point is (x = 50, y = 100)
}

缓慢来自于cos和sin,它们在循环中(不必要地)重复。这可以通过预先计算cos和sin来解决,正如上面提到的SO帖子所述。这意味着巨大的加速(在排名前5的javascript引擎中平均12倍)。

我对各种圆和弧绘制算法进行了非完全可比的速度测试。Bresenham算法速度很快,但需要添加启动和停止准则逻辑,这会使算法速度变慢。如果你真的需要Bresenham和arc,我没有现成的解决方案,也没有找到。这当然是可能的。顺便说一下,与Bresenham相比,使用预计算触发器的step方法在性能上并没有那么差(至少在javascript中)。请用c++测试并报告