从矩形绘制椭圆

时间:2010-05-26 16:16:54

标签: algorithm graphics geometry plot

我一直在网上寻找一种从矩形坐标绘制椭圆的方法,即左上角(x,y)和大小(宽度和高度)。我可以在任何地方找到的唯一的基于Midpoint / Bresenham算法,我不能使用它,因为当使用整数像素时,我会失去精度,因为这些算法使用中心点和径向。

椭圆必须限制在矩形的坐标上,所以如果我给它一个宽度和高度为4(或任何偶数)的矩形,我应该得到一个完全适合4x4矩形的椭圆,而不是一个这将是5x5(就像那些算法给我的那样)。

有谁知道有任何方法可以实现这一目标?

谢谢!

3 个答案:

答案 0 :(得分:4)

你不能得到宽度和高度(除以2)和矩形的中心然后将其插入任何椭圆绘图程序作为其主轴,短轴和中心?我想我一直都没有看到这个问题。

答案 1 :(得分:0)

我发现这个问题的解决方案是绘制具有奇数尺寸的最近的较小椭圆,但沿着偶数长度尺寸拉开一个像素,重复中间像素。

在绘制每个像素时,可以通过使用象限的不同中间点轻松完成此操作:

DrawPixel(midX_high + x, midY_high + y);
DrawPixel(midX_low  - x, midY_high + y);
DrawPixel(midX_high + x, midY_low  - y);
DrawPixel(midX_low  - x, midY_low  - y);

高值是ceil'ed中点,低值是floored中点。

要说明的图像,宽度为15和16的椭圆:

ellipses

答案 2 :(得分:0)

我有同样的需要。这是我的代码解决方案。错误最多为半像素。

ellipses size 1 to 10

我基于我的溶液中的McIlroy ellipse algorithm,一个唯一整数算法麦克罗伊数学证明是精确到半象素,而不丢失或加分,并正确地绘制退化情况例如线和圆。 L. Patrick进一步分析了McIlroy的算法,包括对其进行优化的方法以及如何将填充的椭圆分解为矩形。

麦克罗伊的算法迹线通过所述椭圆的一个象限的路径;其余的象限通过对称渲染。路径中的每个步骤需要三个比较。许多其他椭圆算法使用八分圆代替,这需要每步只有两个比较。然而,基于八分区的方法有是在八分界出了名的不准确的。仅仅节省一次比较就不值得使用八分法的准确性。

像几乎所有其他整数椭圆算法,麦克罗伊的希望的中心在整数坐标,并且轴a的长度和b是整数,以及。但是,我们希望能够绘制椭圆使用任何整数坐标的边界框。具有偶数宽度或甚至高度的边界框将对整数和半的中心坐标,和ab将是一个整数和半

我的解决方案是使用所需整数 double 的整数执行计算。以q开头的任何变量都是根据双像素值计算得出的。偶数q在整数坐标上,奇数q在整数半坐标上。然后,我对麦克罗伊的数学进行了重新处理,以使用这些新的加倍值获得正确的数学表达式。这包括当边界框的宽度或高度均匀时修改起始值。

看,下面给出的子例程/方法drawEllipse。你为它提供了边框的整数坐标(x0y0)和(x1y1)。 x0 <x1x0> x1相对而言并不重要;它将根据需要交换它们。如果您提供x0 == x1,你会得到一个垂直线。对于y0y1坐标类似。您还提供了布尔fill参数,如果为false,则仅绘制椭圆轮廓​​,如果为true,则绘制填充的椭圆。你也必须提供子程序drawPoint(x, y)其中提请的单个点和drawRow(xleft, xright, y),它吸引了来自水平线xleftxright包含性。

McIlroy和Patrick优化了代码以折叠常量,重用常见的子表达式等。为清楚起见,我没有这样做。大多数编译器会自动今天做到这一点呢。

void drawEllipse(int x0, int y0, int x1, int y1, boolean fill)
{
    int xb, yb, xc, yc;


    // Calculate height
    yb = yc = (y0 + y1) / 2;
    int qb = (y0 < y1) ? (y1 - y0) : (y0 - y1);
    int qy = qb;
    int dy = qb / 2;
    if (qb % 2 != 0)
        // Bounding box has even pixel height
        yc++;

    // Calculate width
    xb = xc = (x0 + x1) / 2;
    int qa = (x0 < x1) ? (x1 - x0) : (x0 - x1);
    int qx = qa % 2;
    int dx = 0;
    long qt = (long)qa*qa + (long)qb*qb -2L*qa*qa*qb;
    if (qx != 0) {
        // Bounding box has even pixel width
        xc++;
        qt += 3L*qb*qb;
    }

    // Start at (dx, dy) = (0, b) and iterate until (a, 0) is reached
    while (qy >= 0 && qx <= qa) {
        // Draw the new points
        if (!fill) {
        drawPoint(xb-dx, yb-dy);
        if (dx != 0 || xb != xc) {
            drawPoint(xc+dx, yb-dy);
            if (dy != 0 || yb != yc)
            drawPoint(xc+dx, yc+dy);
        }
        if (dy != 0 || yb != yc)
            drawPoint(xb-dx, yc+dy);
        }

        // If a (+1, 0) step stays inside the ellipse, do it
        if (qt + 2L*qb*qb*qx + 3L*qb*qb <= 0L || 
            qt + 2L*qa*qa*qy - (long)qa*qa <= 0L) {
            qt += 8L*qb*qb + 4L*qb*qb*qx;
            dx++;
            qx += 2;
        // If a (0, -1) step stays outside the ellipse, do it
        } else if (qt - 2L*qa*qa*qy + 3L*qa*qa > 0L) {
            if (fill) {
                drawRow(xb-dx, xc+dx, yc+dy);
                if (dy != 0 || yb != yc)
                    drawRow(xb-dx, xc+dx, yb-dy);
            }
            qt += 8L*qa*qa - 4L*qa*qa*qy;
            dy--;
            qy -= 2;
        // Else step (+1, -1)
        } else {
            if (fill) {
                drawRow(xb-dx, xc+dx, yc+dy);
                if (dy != 0 || yb != yc)
                    drawRow(xb-dx, xc+dx, yb-dy);
            }
            qt += 8L*qb*qb + 4L*qb*qb*qx + 8L*qa*qa - 4L*qa*qa*qy;
            dx++;
            qx += 2;
            dy--;
            qy -= 2;
        }
    }   // End of while loop
    return;
}

上面显示的图像的输出的所有包围盒高达10×10大小。我也跑了所有的椭圆算法高达100×100大小。这在第一象限中产生384614点。其中这些点的每作图和其中实际发生椭圆计算之间的误差。最大误差为0.500000(半像素),所有点的平均误差为0.216597。