您好
我试图使用Bresenham算法绘制一个与正交系统平行的椭圆。我想绘制椭圆的左上角(W,SW,S)四分之一,然后推导出其他部分。
为此,我使用增量算法和二阶逻辑。我是从另一种算法中做到的,它首先绘制了右上角,但我所做的并不起作用。
当第二个区域正在绘制时出现问题,我不知道它来自何处。
你可以看到我拥有的东西(黑色)和我期望的东西(绿色): (椭圆中心(xc,yc)和右上角按钮(x2,y2),在本例中为〜(xc + 30,yc + 20)) (a是abs(x2-xc),b是abs(y2-yc)) 第一个参数是椭圆的中间(xc,yc),第二个参数是右上角建立的x和y半径。你可以看到椭圆走得太远(左边和右边有2个点)。你可以看到另一个例子 (椭圆中心(xc,yc)和右上角按钮(x2,y2),在本例中为〜(xc + 15,yc + 18))
该算法是从具有二阶逻辑的增量算法中推导出来的。
这是我的代码,(a是abs(x2-xc),b是abs(y2-yc))
ellipse(int a, int b, int xc, int yc) {
int a2 = a*a, b2 = b*b;
int x = 0, y = b; //Starting point
int incSW = b2*2 + a2*2;
int deltaW = b2*(-2*x + 3); //deduced from incremental algorithm with the second-order logic
int deltaS = a2*(-2*y + 3);
int deltaSW = deltaW + deltaS;
int d1 = b2 - a2*b + a2/4; //dp starting value in the first region
int d2 = b2*(x - 0.5)*(x - 0.5) + a2*(y - 1)*(y - 1) - a2*b2; //dp starting value in the second region
//First region
while(a2*(y-0.5) >= b2*(-x-1)) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d1>0) {
d1+=deltaSW;
deltaW+=b2*2;
deltaSW+=incSW;
y--;
}
else {
d1+=deltaW;
deltaW+=2*b2;
deltaSW+=2*b2;
}
x--;
}
deltaSW = b2*(2 - 2*x) + a2*(-2*y + 3);
//Second region
while(y>=0) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d2>0) {
d2+=deltaS;
deltaS+=a2*2;
deltaSW+=a2*2;
}
else {
d2+=deltaSW;
deltaSW+=incSW;
deltaS+=a2*2;
x--;
}
y--;
}
}
我希望你能帮助我,谢谢。
答案 0 :(得分:1)
使用误差项e = ax ^ 2 + by ^ 2 - r ^ 2,很容易证明从(x,y)到(x,y + 1)的步长将误差改变为2by + b ,步骤到(x + 1,y + 1)乘2ax + a + 2by + b,步骤到(x + 1,y)乘2ax + a。
从点(-x0,0)开始,从这三个中选择最小绝对误差步长。前两个案例是您称之为“第一区域”的标准。
第一次向右,(x,y)到(x + 1,y)产生的误差最小,你知道你在第二个区域。此时不再需要第一种情况。只使用后两种情况可以完成四分之一椭圆。
请注意,此检查可避免您使用的浮点运算。 Bresenham-ish算法的重点是避免浮点。
最后要注意的是,您不希望每次迭代计算2ax或2by。可以通过维护变量来避免乘法,例如dx = 2ax和dy = 2by,并更新它们。从x到x + 1的步长将dx增加2a,为常数。类似地,从y到y + 1的步长将dy增加2b。
把所有这些放在一起,你得到下面的(粗略)代码。
请注意,您可以通过根据原始错误术语进行验证来检查增量错误计算。如果(x0,0)是初始点,那么你知道x0 ^ 2 = r ^ 2。所以每次迭代的实际误差是* x ^ 2 + b * y ^ 2 - x0 ^ 2。这应该在下面的代码中等于e
,而且确实如此。
import static java.lang.Math.abs;
import java.util.Arrays;
import java.util.function.BiConsumer;
public class EllipseTracer {
static char [] [] raster = new char[51][101];
static void trace(int x, int y, int a, int b, BiConsumer<Integer, Integer> emitter) {
emitter.accept(x, y);
int e = 0;
int dx = 2 * a * x;
int dy = 2 * b * y;
// First region: stepping north and northeast.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eUp = e + dyb;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eUp) < abs(eDg)) {
emitter.accept(x, ++y);
e = eUp;
dy += 2 * b;
} else {
if (abs(eRt) < abs(eDg)) {
// Step east is least error. Found second region.
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
break;
}
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
// Second region: step northeast and east.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eRt) < abs(eDg)) {
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
} else {
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
}
static void emit(int x, int y) {
raster[y][x + 100] = '*';
}
public static void main(String [] args) {
for (int i = 0; i < raster.length; ++i) {
Arrays.fill(raster[i], ' ');
}
trace(-100, 0, 1, 4, EllipseTracer::emit);
for (int i = 0; i < raster.length; ++i) {
System.out.println(raster[i]);
}
}
}
你可以添加更多技巧来避免绝对值,但我会让你找那些。