В целочисленной математике обычная параметризация заключается в использовании ограничивающих линий (в направлении по часовой стрелке или против часовой стрелки) вместо ваших углов. Итак, если вы можете преобразовать эти углы в такие (вам нужно sin,cos
для этого, но только один раз), то вы можете использовать для этого рендеринг на основе целочисленной математики. Как я уже упоминал в комментарии, брезенхем не является хорошим подходом для сектора эллипса, так как вам нужно будет вычислить состояние внутренних итераторов и счетчиков для начальной точки интерполяции, а также он даст вам только точки окружности вместо заполненной формы.
Для этого есть много подходов, вот простой:
преобразовать эллипс в круг
просто путем изменения масштаба оси меньшего радиуса
перебрать блок такого круга
простые 2 вложенные for
петли, охватывающие вписанный квадрат в нашу окружность
проверить, находится ли точка внутри круга
просто проверьте, есть ли x^2 + y^2 <= r^2
, когда круг находится в центре (0,0)
проверить, лежит ли точка между линиями края
поэтому он должен быть по часовой стрелке с одним краем и против часовой стрелки с другим. Вы можете использовать для этого перекрестное произведение (его полярность координаты z скажет вам, находится ли точка по часовой стрелке или против часовой стрелки относительно тестируемой линии края)
но это будет работать только до 180-градусных срезов, поэтому вам также нужно добавить некоторую проверку квадрантов, чтобы избежать ложных отрицательных результатов. Но это всего лишь несколько «если».
если все условия соблюдены, преобразуйте точку обратно в эллипс и визуализируйте
Вот небольшой пример 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
, это будет направление по часовой стрелке, которое соответствует маршруту. Если вы используете 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 градусов...
person
Spektre
schedule
27.09.2018