При целочисленной математике обычная параметризация - использование ограничительных линий (в направлении CW или CCW) вместо ваших углов.Так что если вы можете преобразовать эти углы в такие (вам нужно sin,cos
для этого, но только один раз), то вы можете использовать для этого рендеринг с целочисленной математикой.Как я уже упоминал в комментарии, bresenham не является хорошим подходом для сектора эллипса, так как вам нужно будет вычислить состояние внутренних итераторов и счетчиков для начальной точки интерполяции, а также даст вам только точки окружности вместо заполненной формы.
Есть много подходов для этого, здесь простой:
преобразовать эллипс в круг
просто путем изменения масштабаменьшая ось радиуса
проходить через bbox такого круга
простые 2 вложенные for
петли, охватывающие вписанный квадрат нашего круга
проверьте, находится ли точка внутри круга
, просто проверьте, если x^2 + y^2 <= r^2
, а круг центрируется по (0,0)
проверьте, находится ли точка между линиями ребер
, поэтому она должна быть CW с одним ребром и CCW с другим.Для этого вы можете использовать перекрестное произведение (его полярность по координате z скажет вам, является ли точка CW или CCW относительно тестируемой линии края)
, но это будет работать только до срезов 180 градусов, поэтому вам также необходимо добавить некоторыепроверка квадрантов, чтобы избежать ложных негативов.Но это всего лишь несколько ifs поверх этого.
, если все условия выполнены, преобразовать точку обратно в эллипс и отобразить
Вот небольшой C ++ пример этого:
void elliptic_arc(int x0,int y0,int rx,int ry,int a0,int a1,DWORD c)
{
// variables
int x, y, r,
xx,yy,rr,
xa,ya,xb,yb, // a0,a1 edge points with radius r
mx,my,cx,cy,sx,sy,i,a;
// my Pixel access (you can ignore it and use your style of gfx access)
int **Pixels=Main->pyx; // Pixels[y][x]
int xs=Main->xs; // resolution
int ys=Main->ys;
// init variables
r=rx; if (r<ry) r=ry; rr=r*r; // r=max(rx,ry)
mx=(rx<<10)/r; // scale from circle to ellipse (fixed point)
my=(ry<<10)/r;
xa=+double(r)*cos(double(a0)*M_PI/180.0);
ya=+double(r)*sin(double(a0)*M_PI/180.0);
xb=+double(r)*cos(double(a1)*M_PI/180.0);
yb=+double(r)*sin(double(a1)*M_PI/180.0);
// render
for (y=-r,yy=y*y,cy=(y*my)>>10,sy=y0+cy;y<=+r;y++,yy=y*y,cy=(y*my)>>10,sy=y0+cy) if ((sy>=0)&&(sy<ys))
for (x=-r,xx=x*x,cx=(x*mx)>>10,sx=x0+cx;x<=+r;x++,xx=x*x,cx=(x*mx)>>10,sx=x0+cx) if ((sx>=0)&&(sx<xs))
if (xx+yy<=rr) // inside circle
{
if ((cx>=0)&&(cy>=0)) a= 0;// actual quadrant
if ((cx< 0)&&(cy>=0)) a= 90;
if ((cx>=0)&&(cy< 0)) a=270;
if ((cx< 0)&&(cy< 0)) a=180;
if ((a >=a0)||((cx*ya)-(cy*xa)<=0)) // x,y is above a0 in clockwise direction
if ((a+90<=a1)||((cx*yb)-(cy*xb)>=0))
Pixels[sy][sx]=c;
}
}
, знайте, что оба угла должны быть в диапазоне <0,360>
.У моего экрана буква y направлена вниз, поэтому, если a0<a1
, это будет направление CW, соответствующее пути.Если вы используете a1<a0
, то диапазон будет пропущен, а остальная часть эллипса будет визуализирована взамен.
Этот подход использует a0,a1
в качестве реальных углов !!!
Чтобы избежать разрывов внутрицикл Я вместо этого использовал 10-битные шкалы с фиксированной точкой.
Вы можете просто разделить это на 4 квадранта, чтобы избежать 4, если внутри циклов для повышения производительности.
x,y
- точка в круговойшкала с центром в (0,0)
cx,cy
- точка в эллиптической шкале с центром в (0,0)
sx,sy
- точка в эллиптической шкале, переведенная в центральное положение эллипса
Осторожно мой пиксельный доступ Pixels[y][x]
, но большинство API-интерфейсов используют Pixels[x][y]
, поэтому не забудьте изменить его на API, чтобы избежать нарушений прав доступа или поворота результата на 90 градусов ...