我一直在网上寻找一种方法,可以从矩形坐标(即左上角(x,y)和大小(宽度和高度))绘制一个椭圆。到处都是基于Midpoint / Bresenham算法的方法,但我不能使用这些算法,因为在使用整数像素时,会因为这些算法使用中心点和半径而失去精度。
椭圆必须限制在矩形坐标内。因此,如果我提供一个宽度和高度均为4(或任何偶数)的矩形,则应该获得完全适合4x4矩形的椭圆,而不是5x5的椭圆(与那些算法给我的结果相反)。
有没有人知道如何完成这个任务呢?
谢谢!
椭圆必须限制在矩形坐标内。因此,如果我提供一个宽度和高度均为4(或任何偶数)的矩形,则应该获得完全适合4x4矩形的椭圆,而不是5x5的椭圆(与那些算法给我的结果相反)。
有没有人知道如何完成这个任务呢?
谢谢!
您可以获取矩形的宽度和高度(除以2),并将其作为椭圆绘制例程的长轴、短轴和中心进行插入吗?我想我没有完全看到问题所在。
a
和b
也必须是整数。然而,我们希望能够使用任意整数坐标绘制一个带有边界框的椭圆。当边界框的宽度或高度为偶数时,其中心将位于一个整数半坐标上,而a
或b
将是一个整数半加一。q
开头的变量都是从双像素值计算出来的。偶数的q
变量在整数坐标上,奇数的q
变量在整数半坐标上。然后,我重新修改了McIlroy的数学公式,以这些新的双倍值得到正确的数学表达式。这包括在边界框具有偶数宽度或高度时修改起始值。drawEllipse
。您需要提供边界框的整数坐标(x0
, y0
)和(x1
, y1
)。它不关心x0
< x1
还是x0
> x1
;它会根据需要进行交换。如果提供x0
== x1
,则会得到一条垂直线。对于y0
和y1
坐标也是如此。您还需要提供布尔值fill
参数,如果为false,则仅绘制椭圆轮廓,如果为true,则绘制填充椭圆。您还必须提供子程序drawPoint(x, y)
,该子程序绘制单个点以及drawRow(xleft, xright, y)
子程序,该子程序从xleft
到xright
(包括两端)绘制水平线。 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;
}
dx * 2 + (even_pixel_width ? 2 : 1)
。最后,在函数开头创建一个bool even_pixel_width
,初始化为false,然后在if(qx!=0)
下添加even_pixel_width=true
。 - deLockdrawRow
的第三个参数是行的y坐标,而不是它的宽度。甚至像素宽度的调整已经在代码中了;没有必要为此创建布尔标志。 - DrSheldon