我正在构建一个用于绘制ASCII艺术的库。鉴于我有一个3 x 3图表,其中每个点代表一个像素,一条线从点0,0
开始,并且对角线到2,2
(左下角点到右上角点)。
如果我画画布,它看起来像这样:
2 / Points: 2,0 2,1 2,2
1 / 1,0 1,1 2,1
0 / 0,0 1,0 2,0
0 1 2
我现在想要构建一个可以顺时针向右旋转线的算法,所以如果我将算法应用到线上,结果应该是:
2 Points: 2,0 2,1 2,2
1 1,0 1,1 2,1
0 _ _ _ 0,0 1,0 2,0
0 1 2
所以基本上这条线向右旋转45度,产生一条水平线。
我认为我需要一个旋转矩阵,如http://mathworld.wolfram.com/RotationMatrix.html中所述,但数学有点高于我的头脑。
任何人都有一个关于如何工作的简单解释,给定我的2D坐标系,可能是伪代码?
答案 0 :(得分:1)
如果使用矩阵则无关紧要。这里是简单的C ++示例,没有使用我在评论中提到的所有矩阵:
//---------------------------------------------------------------------------
const int xs=32;
const int ys=32;
char pic[xs][ys];
//---------------------------------------------------------------------------
void cls();
void rot45cw();
void rot90cw();
//---------------------------------------------------------------------------
void cls()
{
int x,y;
// clear screen
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
pic[x][y]=' ';
// add diagonal line for testing
for (x=xs/2;(x<xs)&&(x<ys);x++) pic[x][x]='\\';
}
//---------------------------------------------------------------------------
void rot45cw()
{
int x,y,ix,iy,x0,y0;
float fx,fy,a,c,s;
char tmp[xs][ys],q;
a=-45.0*M_PI/180.0; // rotation angle [rad]
x0=xs/2; // center of rotation
y0=ys/2;
c=cos(a); s=sin(a);
// copy pic to tmp
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
tmp[x][y]=pic[x][y];
// rotate
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
// offset so (0,0) is center of rotation
fx=x-x0;
fy=y-y0;
// rotate (fx,fy) by ang
ix=float((fx*c)-(fy*s));
iy=float((fx*s)+(fy*c));
// offset back
ix+=x0;
iy+=y0;
// transform tmp to pic
if ((ix>=0)&&(ix<xs)&&(iy>=0)&&(iy<ys)) q=tmp[ix][iy]; else q=' ';
if (q=='/') q='\\';
else if (q=='\\') q='/';
else if (q=='-') q='|';
else if (q=='|') q='-';
pic[x][y]=q;
}
}
//---------------------------------------------------------------------------
void rot90cw()
{
int x,y,ix,iy,x0,y0;
char tmp[xs][ys],q;
// center of rotation
x0=xs/2;
y0=ys/2;
// copy pic to tmp
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
tmp[x][y]=pic[x][y];
// rotate
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
// rotate
iy=x0-(x-x0);
ix=y0+(y-y0);
// transform tmp to pic
if ((ix>=0)&&(ix<xs)&&(iy>=0)&&(iy<ys)) q=tmp[ix][iy]; else q=' ';
if (q=='-') q='\\';
else if (q=='\\') q='|';
else if (q=='|') q='/';
else if (q=='/') q='-';
pic[x][y]=q;
}
}
//---------------------------------------------------------------------------
和用法:
// clear and add diagonal line for testing once:
cls();
// and this do in some timer or whatever:
//rot45cw();
rot90cw();
这里90deg预览:
这里45deg预览:
正如您所看到的,45deg旋转是一个问题,因为它不是1:1映射,所以一些单元格将映射到多个单元格。对于固定分辨率,您可以手动执行1:1的映射,但我怀疑它是否可以通过算法轻松实现动态分辨率。
是您正在使用(3x3)地图,可以进行45度旋转,但问题仍然存在,因为当您映射单个像素时,有些像素会被再次复制,如果您考虑到您正在查看字符,那么它看起来会很糟糕。
如果我把所有东西放在一起,我宁愿只使用90度旋转,除非你得到图像的矢量表示......
可以使用LUT加速字符旋转
[Edit1] 45度旋转
我更多地教它并找出45度旋转的解决方案。您必须使用不同的旋转内核。不是围绕圆圈旋转,而是围绕正方形旋转1/8周长。为了更好地理解这里的小例子:
// r=1
0 1 2 7 0 1
7 3 -> 6 2
6 5 4 5 4 3
// r=2
0 1 2 3 4 E F 0 1 2
F 5 D 3
E 6 -> C 4
D 7 B 5
C B A 9 8 A 9 8 7 6
这是1:1的映射,所以没有问题。 C ++中的代码如下所示:
//---------------------------------------------------------------------------
void rot45cw()
{
int x0,y0,ax,ay,ad,bx,by,bd,a,b,i,r,rs;
char tmp[xs][ys],q;
// rotation kernel 4 directions
const int dx[4]={ 0,-1, 0,+1};
const int dy[4]={-1, 0,+1, 0};
// center of rotation
x0=xs/2;
y0=ys/2;
// copy pic to tmp
for (ay=0;ay<ys;ay++)
for (ax=0;ax<xs;ax++)
tmp[ax][ay]=pic[ax][ay];
// rotate all "screws" to fill entire map
rs=xs; if (rs<ys) rs=ys;
for (r=1;r<rs;r++)
{
ax=x0+r; ay=y0+r; ad=0; a=0; // start position a
bx=x0 ; by=y0+r; bd=3; b=r; // start position b
for (i=8*r;i>0;i--) // process one screw
{
// fetch and convert processed character
if ((ax>=0)&&(ax<xs)&&(ay>=0)&&(ay<ys))
if ((bx>=0)&&(bx<xs)&&(by>=0)&&(by<ys))
{
q=tmp[ax][ay];
if (q=='-') q='\\';
else if (q=='\\') q='|';
else if (q=='|') q='/';
else if (q=='/') q='-';
pic[bx][by]=q;
}
// update position
ax+=dx[ad]; bx+=dx[bd];
ay+=dy[ad]; by+=dy[bd];
// update direction
a++; if (a>=r+r) { a=0; ad=(ad+1)&3; }
b++; if (b>=r+r) { b=0; bd=(bd+1)&3; }
}
}
// fetch and convert center of rotation
if ((x0>=0)&&(x0<xs)&&(y0>=0)&&(y0<ys))
{
q=pic[x0][y0];
if (q=='-') q='\\';
else if (q=='\\') q='|';
else if (q=='|') q='/';
else if (q=='/') q='-';
pic[x0][y0]=q;
}
}
//---------------------------------------------------------------------------
void rot45ccw()
{
int x0,y0,ax,ay,ad,bx,by,bd,a,b,i,r,rs;
char tmp[xs][ys],q;
// rotation kernel 4 directions
const int dx[4]={ 0,-1, 0,+1};
const int dy[4]={-1, 0,+1, 0};
// center of rotation
x0=xs/2;
y0=ys/2;
// copy pic to tmp
for (ay=0;ay<ys;ay++)
for (ax=0;ax<xs;ax++)
tmp[ax][ay]=pic[ax][ay];
// rotate all "screws" to fill entire map
rs=xs; if (rs<ys) rs=ys;
for (r=1;r<rs;r++)
{
ax=x0+r; ay=y0+r; ad=0; a=0; // start position a
bx=x0 ; by=y0+r; bd=3; b=r; // start position b
for (i=8*r;i>0;i--) // process one screw
{
// fetch and convert processed character
if ((ax>=0)&&(ax<xs)&&(ay>=0)&&(ay<ys))
if ((bx>=0)&&(bx<xs)&&(by>=0)&&(by<ys))
{
q=tmp[bx][by];
if (q=='-') q='/';
else if (q=='/') q='|';
else if (q=='|') q='\\';
else if (q=='\\') q='-';
pic[ax][ay]=q;
}
// update position
ax+=dx[ad]; bx+=dx[bd];
ay+=dy[ad]; by+=dy[bd];
// update direction
a++; if (a>=r+r) { a=0; ad=(ad+1)&3; }
b++; if (b>=r+r) { b=0; bd=(bd+1)&3; }
}
}
// fetch and convert center of rotation
if ((x0>=0)&&(x0<xs)&&(y0>=0)&&(y0<ys))
{
q=pic[x0][y0];
if (q=='-') q='/';
else if (q=='/') q='|';
else if (q=='|') q='\\';
else if (q=='\\') q='-';
pic[x0][y0]=q;
}
}
//---------------------------------------------------------------------------
dx,dy
表格简单地类似于sin
和cos
。最后这里是预览:
但如果你围绕它的中心旋转方形,它会粗糙,它不会像你预期的那样!这是CCW的例子: