因此,让我们测试与计算的网格线的交点floats
(正如我在评论中建议的那样)。我会先将圆圈分成 4 个部分(“象限”),如下所示:
在每个部分中,只有一个轴是主要轴(总是与另一个轴有更大或相等的差异),因此我们可以通过递增直接迭代它(不会在曲线上创建孔)。然后使用圆方程计算短轴:
y = sqrt( r*r - (x-x0)*(x-x0) )
x = sqrt( r*r - (y-y0)*(y-y0) )
所以只需按4x(非嵌套)循环顺序循环长轴for
并添加点。然而,每当计算的短轴不直接在网格上时,我们需要添加 2 个点,其中第二个点已递减长轴。这可能会导致重复点,因此为避免重复,我们还应该检查添加的点是否尚未添加到列表的末尾。在上一节的末尾,我们还应该检查列表的开头(或删除最后几个重复点)我认为最多会有 4 个这样的点。
当将所有这些放在 C++/VCL 中时,它看起来像这样:
//---------------------------------------------------------------------------
// global grid
float gx0=0.0; // cell origin
float gy0=0.0;
float gxs=30.0; // cell size
float gys=20.0;
//---------------------------------------------------------------------------
// grid -> screen | screen -> grid
// sx = gx0 + gx*gxs | gx = (sx-gx0)/gxs
// sy = gy0 + gy*gys | gy = (sy-gy0)/gys
//---------------------------------------------------------------------------
void draw_cell(TCanvas *scr,int x,int y,DWORD col) // screen, cell, color
{
x=floor(gx0+(float(x)*gxs));
y=floor(gy0+(float(y)*gys));
scr->Pen->Color=clDkGray;
scr->Brush->Color=TColor(col);
scr->Rectangle(x,y,x+gxs,y+gys);
}
//---------------------------------------------------------------------------
void draw_grid(TCanvas *scr,int xs,int ys,DWORD col) // screen, its resolution, color
{
int x0,y0,x1,y1,x,y;
// visible grid
x0=floor((float( 0)-gx0)/gxs);
y0=floor((float( 0)-gy0)/gys);
x1= ceil((float(xs)-gx0)/gxs);
y1= ceil((float(ys)-gy0)/gys);
for (y=y0;y<=y1;y++)
for (x=x0;x<=x1;x++)
draw_cell(scr,x,y,col);
}
//---------------------------------------------------------------------------
void draw_circle(TCanvas *scr,float cx,float cy,float r,DWORD col) // screen, circle, color
{
int i,ix,iy;
int *pnt,pnts=0; // output list of cells { x0,y0, x1,y1, ... }
float x,y,x0,y0,x1,y1,xx,yy,rr=r*r;
// ranges where x or y is major axis
x0=floor(cx-(r*sqrt(0.5)));
y0=floor(cy-(r*sqrt(0.5)));
x1=ceil (cx+(r*sqrt(0.5)));
y1=ceil (cy+(r*sqrt(0.5)));
// prepare list
pnt=new int[4*(x1-x0+1)];
// this adds point (px,py) if the pnt[pi] ... pnt[pi-6] in list is not the same as (px,py)
#define pnt_add(pi,px,py) \
{ \
int e=1; \
if (pi>=0) \
{ \
if ((pi+1<pnts)&&((pnt[pi+0]==px)&&(pnt[pi+1]==py))) e=0; \
if ((pi-1<pnts)&&((pnt[pi-2]==px)&&(pnt[pi-1]==py))) e=0; \
if ((pi-3<pnts)&&((pnt[pi-4]==px)&&(pnt[pi-3]==py))) e=0; \
if ((pi-5<pnts)&&((pnt[pi-6]==px)&&(pnt[pi-5]==py))) e=0; \
} \
if (e){ pnt[pnts]=px; pnts++; pnt[pnts]=py; pnts++; } \
}
// rasterize "quadrants" in order so no sorting is needed later
for (x=x0;x<x1;x++)
{
xx=x-cx; xx*=xx; y=rr-xx; if (y<0.0) continue;
y=sqrt(y); iy=float(cy-y); ix=x;
if (y-floor(y)>1e-10) pnt_add(pnts-2,ix-1,iy);
pnt_add(pnts-4,ix ,iy);
}
for (y=y0;y<y1;y++)
{
yy=y-cy; yy*=yy; x=rr-yy; if (x<0.0) continue;
x=sqrt(x); ix=float(cx+x); iy=y;
if (x-floor(x)>1e-10) pnt_add(pnts-2,ix,iy-1);
pnt_add(pnts-4,ix,iy );
}
for (x=x1;x>x0;x--)
{
xx=x-cx; xx*=xx; y=rr-xx; if (y<0.0) continue;
y=sqrt(y); iy=float(cy+y); ix=x;
pnt_add(pnts-2,ix ,iy);
if (y-floor(y)>1e-10) pnt_add(pnts-4,ix-1,iy);
}
for (y=y1;y>y0;y--)
{
yy=y-cy; yy*=yy; x=rr-yy; if (x<0.0) continue;
x=sqrt(x); ix=float(cx-x); iy=y;
pnt_add(pnts-2,ix,iy );
if (x-floor(x)>1e-10) pnt_add(pnts-4,ix,iy-1);
}
// check duplicity between pnt start and end
for (i=pnts-8;i+1<pnts;i+=2)
if (pnt[0]==pnt[i+0])
if (pnt[1]==pnt[i+1])
{
pnts=i;
break;
}
// render the list (continuously change color to show points order)
for (i=0;i+1<pnts;i+=2) draw_cell(scr,pnt[i+0],pnt[i+1],0x010000*(25+((230*i)/pnts)));
// ---- you can ignore all the rest of the code its my debug rendering -----
// debug numbers
scr->Font->Color=TColor(0x0000FF00);
scr->Font->Height=gys;
scr->Brush->Style=bsClear;
for (i=0;i+1<pnts;i+=2)
{
x=gx0+float(pnt[i+0])*gxs;
y=gy0+float(pnt[i+1])*gys;
scr->TextOutA(x,y,i>>1);
}
scr->Brush->Style=bsSolid;
// debug circle overlay (ignore this)
if (1)
{
int x,y,rx,ry,w=8;
x =floor(gx0+(cx*gxs));
y =floor(gy0+(cy*gys));
rx=floor(r*gxs);
ry=floor(r*gys);
scr->Pen->Color=clYellow;
scr->Brush->Style=bsClear;
scr->Ellipse(x-rx,y-ry,x+rx,y+ry);
scr->MoveTo(x-w,y);
scr->LineTo(x+w,y);
scr->MoveTo(x,y-w);
scr->LineTo(x,y+w);
scr->Brush->Style=bsSolid;
}
// free the list you should store/copy/export it somwhere
delete[] pnt;
#undef pnt_add
}
//---------------------------------------------------------------------------
这是输出(包括我的调试渲染):
结果是在pnt[pnts]
数组x,y
中按顺序保存网格坐标...
这远未优化,例如:
- 每个
sqrt
计算两次,你可以记住它们......
- 每个
sqr
刚刚增加的值都可以在没有乘法的情况下计算