我一直在网上寻找一种从矩形坐标绘制椭圆的方法,即左上角(x,y)和大小(宽度和高度)。我可以在任何地方找到的唯一的基于Midpoint / Bresenham算法,我不能使用它,因为当使用整数像素时,我会失去精度,因为这些算法使用中心点和径向。
椭圆必须限制在矩形的坐标上,所以如果我给它一个宽度和高度为4(或任何偶数)的矩形,我应该得到一个完全适合4x4矩形的椭圆,而不是一个这将是5x5(就像那些算法给我的那样)。
有谁知道有任何方法可以实现这一目标?
谢谢!
答案 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的椭圆:
答案 2 :(得分:0)
我有同样的需要。这是我的代码解决方案。错误最多为半像素。
我基于我的溶液中的McIlroy ellipse algorithm,一个唯一整数算法麦克罗伊数学证明是精确到半象素,而不丢失或加分,并正确地绘制退化情况例如线和圆。 L. Patrick进一步分析了McIlroy的算法,包括对其进行优化的方法以及如何将填充的椭圆分解为矩形。
麦克罗伊的算法迹线通过所述椭圆的一个象限的路径;其余的象限通过对称渲染。路径中的每个步骤需要三个比较。许多其他椭圆算法使用八分圆代替,这需要每步只有两个比较。然而,基于八分区的方法有是在八分界出了名的不准确的。仅仅节省一次比较就不值得使用八分法的准确性。
像几乎所有其他整数椭圆算法,麦克罗伊的希望的中心在整数坐标,并且轴a
的长度和b
是整数,以及。但是,我们希望能够绘制椭圆使用任何整数坐标的边界框。具有偶数宽度或甚至高度的边界框将对整数和半的中心坐标,和a
或b
将是一个整数和半
我的解决方案是使用所需整数 double 的整数执行计算。以q
开头的任何变量都是根据双像素值计算得出的。偶数q
在整数坐标上,奇数q
在整数半坐标上。然后,我对麦克罗伊的数学进行了重新处理,以使用这些新的加倍值获得正确的数学表达式。这包括当边界框的宽度或高度均匀时修改起始值。
看,下面给出的子例程/方法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;
}
上面显示的图像的输出的所有包围盒高达10×10大小。我也跑了所有的椭圆算法高达100×100大小。这在第一象限中产生384614点。其中这些点的每作图和其中实际发生椭圆计算之间的误差。最大误差为0.500000(半像素),所有点的平均误差为0.216597。