旋转位矩阵

时间:2015-05-01 03:03:19

标签: java game-engine affinetransform

让我们说我使用大小为8的字符数组来表示图像的碰撞掩码。 char的每个位代表一个像素。实际上,我将使用长[64]阵列作为64x64矩阵。

所以框会显示为:

00000000
01111110
01111110
01111110
01111110
01111110
01111110
00000000

45度的示例输出应该如下所示,尽管旋转可以是任何角度。这种形状对于45度旋转可能不准确,正如我手工完成的那样。

00011000
00111100
01111110
11111111
11111111
01111110
00111100
00011000

另一个示例输出向右旋转小 - 10度?这些数值可能是错误的,从数学角度来说,我不知道它是如何旋转的,但我认为可以安全地假设如果每个钻头的旧形状覆盖率超过50%,那么它就会出现问题。 ; ld是1。

00000000
00111111
01111111
01111110
01111110
11111110
11111100
00000000

在没有旋转的情况下,使用此stackoverflow回复中定义的位移来快速找到这些位掩码之间的冲突:https://stackoverflow.com/a/336615/4595736

现在,我过去使用过Path2D,Rectangles,Shapes等...并使用AffineTransform来旋转对象。 Path2D是唯一一个提供我想要的复杂形状的类,但是它的链接列表就像#34;访问每个点的行为并不像我希望的那样快。

在Java中旋转二进制映射的最佳方法是什么?

对于Matrices来说,Java库似乎也不是最快的。

2 个答案:

答案 0 :(得分:3)

此解决方案基于prior answer。它不是将输入点映射到输出点,而是将输出点映射到输入矩阵空间中的位置。

在此版本中,它只使用最接近的整数索引点的值。通过更复杂的值计算可以获得更好的结果,该计算使用邻居点的值的距离加权和。

以下是一些结果:

Angle: 10.0 degrees
00000000 00000000
00000000 00000000
00111100 00011000
00111100 00011110
00111100 00111110
00111100 00111100
00000000 00001100
00000000 00000000

Angle: 45.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000001000000000
00000000000000000000 00000000011100000000
00000111111111100000 00000000111110000000
00000111111111100000 00000001111111000000
00000111111111100000 00000011111111100000
00000111111111100000 00000111111111110000
00000111111111100000 00001111111111111000
00000111111111100000 00011111111111111100
00000111111111100000 00001111111111111000
00000111111111100000 00000111111111110000
00000111111111100000 00000011111111100000
00000111111111100000 00000001111111000000
00000000000000000000 00000000111110000000
00000000000000000000 00000000011100000000
00000000000000000000 00000000001000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

Angle: 10.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000111111111100000 00000011111000000000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000111111111110000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000111111111100000 00000111111111100000
00000000000000000000 00000000001111100000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

Angle: 90.0 degrees
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000111111111100000 00000011111111110000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000
00000000000000000000 00000000000000000000

测试程序:

public class Test {
  public static void main(String args[]) {
    int[][] input1 = { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0 },
        { 0, 0, 1, 1, 1, 1, 0, 0 }, { 0, 0, 1, 1, 1, 1, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } };

    testit(input1, 10);

    int[][] input2 = new int[20][20];
    for(int i=5; i<15; i++){
      for(int j = 5; j<15; j++){
        input2[i][j] = 1;
      }
    }

    testit(input2, 45);
    testit(input2, 10);
    testit(input2, 90);
  }

  private static void testit(int[][] input, double degrees) {
    int[][] output = rotate(input, degrees);
    System.out.println("Angle: "+degrees+" degrees");
    for (int i = 0; i < input.length; i++) {
      for (int j = 0; j < input[i].length; j++) {
        System.out.print(input[i][j]);
      }
      System.out.print(" ");
      for (int j = 0; j < output[i].length; j++) {
        System.out.print(output[i][j]);
      }
      System.out.println();
    }
    System.out.println();
  }

  private static int[][] rotate(int[][] input, double degrees) {

    double rad = Math.toRadians(degrees);
    double sin = Math.sin(-rad);
    double cos = Math.cos(-rad);

    int[][] output = new int[input.length][input[0].length];

    for (int i = 0; i < output.length; i++) {
      double oldX = i - output.length / 2.0; // move to center
      for (int j = 0; j < input[i].length; j++) {
        {
          double oldY = j - output[i].length / 2.0; // move to center
          double x = (int) (cos * oldX + sin * oldY + input.length / 2.0);
          double y = (int) (-sin * oldX + cos * oldY + input[i].length / 2.0);
          output[i][j] = getNearestVal(input, x, y);
        }
      }
    }
    return output;
  }

  private static int getNearestVal(int[][] input, double x, double y) {
    int xLow = (int) Math.floor(x);
    int xHigh = (int) Math.ceil(x);
    int yLow = (int) Math.floor(y);
    int yHigh = (int) Math.ceil(y);
    int[][] points = { { xLow, yLow }, { xLow, yHigh }, { xHigh, yLow },
        { xHigh, yHigh } };
    double minDistance = Double.POSITIVE_INFINITY;
    int minDistanceValue = 0;
    for (int[] point : points) {
      double distance = (point[0] - x) * (point[0] - x) + (point[1] - y)
          * (point[1] - y);
      if (distance < minDistance) {
        minDistance = distance;
        if (point[0] >= 0 && point[0] < input.length && point[1] >= 0
            && point[1] < input[point[0]].length) {
          minDistanceValue = input[point[0]][point[1]];
        } else {
          minDistanceValue = 0;
        }
      }
    }
    return minDistanceValue;
  }
}

答案 1 :(得分:1)

我同意通常最好映射输出的条目,但这足以检测碰撞。并且您可以将内部点设置为0以使其更加稀疏(如果您没有可以跳入其他对象的非常小的对象):

...

// simple algorithm to remove inner 1s with a sliding window,
// here shown with 3x3 but I think it has to be 5x5 (you can omit the corners)
int[][] inputSparse = new int[input.length][input[0].length];
// assuming the border is 0 anyway
// not the best way to implement it, but it shows the idea and it only has to be done once
for (int i = 1; i < inputSparse.length - 1; i++) {
  for (int j = 1; j < inputSparse[0].length - 1; j++) {
    if (input[i-1][j-1] != 1 || input[i-1][j] != 1 || input[i-1][j+1] !=1 ||
        input[i][j-1] != 1 || input[i][j] != 1 || input[i][j+1] != 1 ||
        input[i+1][j-1] != 1 || input[i+1][j] != 1 || input[i+1][j+1] !=1) {
      inputSparse[i][j] = input[i][j];
    } else {
      inputSparse[i][j] = 0; // just to show that a one is removed, you don't need the else
    }
  }
}
...
output = rotate(inputSparse, 10); // example
...
private int[][] rotate(int[][] input, double degrees) {
  double rad = Math.toRadians(degrees);
  double sin = Math.sin(rad);
  double cos = Math.cos(rad);
  int[][] output = new int[input.length][input[0].length];
  for (int i = 0;  i < input.length; i++) {
    double oldY = i - (input.length - 1) / 2.0;
    for (int j = 0; j < input[0].length; j++) {
      if (input[i][j] == 1) { // <-- this is the big gain !!!
        double oldX = j - (input[0].length - 1) / 2.0;
        int x = (int)(cos * oldX + sin * oldY + input[0].length / 2.0);
        int y = (int)(-sin * oldX + cos * oldY + input.length / 2.0);
        output[y][x] = 1;
      }
    }
  }
  return output;
}

旧答案: 我不知道这是否足够好,但你只能改变它们,希望它有任何意义,我不知道你是否会得到&#34;漏洞&#34; (在两者之间为0)并且这只有在你周围有足够的0或者索引会超出界限时才有效,无论如何这是我的建议:

int[][] input = {{0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 1, 1, 1, 1, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0},
                 {0, 0, 0, 0, 0, 0, 0, 0}};

double rad = Math.toRadians(10); // 10 * Math.PI / 180;
double sin = Math.sin(rad);
double cos = Math.cos(rad);

int[][] output = new int[8][8];
// or: int[][] output = new int[input.lengh][input[0].lengh];

for (int i = 0;  i < 8; i++) {
  double oldX = i - 3.5; // move to center
  for (int j = 0; j < 8; j++) {
   if (input[i][j] == 1) {
     double oldY = j - 3.5; // move to center
     int x = (int)(cos * oldX + sin * oldY + 4); // + 3.5 to shift back, +0.5 to simulate rounding
     int y = (int)(-sin * oldX + cos * oldY + 4);
     output[x][y] = 1;
   }
  }
}