algorithm - 在 2D 3 x 3 网格中旋转对角线 - 需要旋转矩阵吗?

标签 algorithm

我正在构建一个用于绘制 ASCII 艺术的库。假设我有一个 3 x 3 的图形,其中每个点代表一个像素,一条线从点 0,0 开始并沿对角线指向 2,2(左下角指向右上角)。

如果我绘制 Canvas ,它看起来像这样:

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 坐标系(可能是伪代码)简单解释一下这是如何工作的?

最佳答案

是否使用矩阵并不重要。这里没有矩阵的简单 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();

此处为 90 度预览:

90cw

这里是 45deg 预览:

45cw

如您所见,45 度旋转是一个问题,因为它不是 1:1 映射,因此一些单元格将映射到多个单元格中。对于固定分辨率,您可以手动进行一些 1:1 映射,但我怀疑它能否通过算法轻松实现动态分辨率。

是的 您只使用了 (3x3) map ,其中 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 表只是类比sincos。最后是预览:

rot45cw square kernel

但是如果你围绕它的中心旋转方形,它就不会像你预期的那样粗糙!这里是 CCW 示例:

rectangle

关于algorithm - 在 2D 3 x 3 网格中旋转对角线 - 需要旋转矩阵吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40353932/

相关文章:

algorithm - 语言检测如何工作?

algorithm - 找出包含 1 和 0 一样多的最长子序列 O(n)

c++ - C++ 中的简单数组使用?

python - 生成器理解 Python 中的前瞻算法

使用 Java 进行高清和标清图像的图像比较

Java - 在对象列表中搜索 2 个日期之间的日期

algorithm - 如何优化 Knuth Morris Pratt 字符串匹配算法

algorithm - 再现具有原始形状的图像。 (图形优化问题)

c++ - 在给定数组中找到长度为 k 的所有连续子数组的总和

python - 前缀和算法