How to calculate all 24 rotations of 3d array?

后端 未结 9 745
清酒与你
清酒与你 2021-02-12 10:45

I have a 3d numpy array describing a polycube (imagine a 3d tetris piece). How can I calculate all 24 rotations?

Numpy\'s array manipulation routines includes a rot90 me

9条回答
  •  遥遥无期
    2021-02-12 11:12

    Edit: As my solution basically boils down to the product of the parities of the axes multiplied by the parity of the permutation of the axes, the simplest method for generating all of the regular rotations of an n-dimensional array is this (swiping some code form @Divakar's answer):

    import itertools as it
    
    def p_parity(a):
        a = np.asarray(a)
        l = a.size
        i, j = np.tril_indices(l, -1)
        return np.product(np.sign(a[i] - a[j]))
    
    def rotations_gen(m):
        n = m.ndim
        for i in it.product([-1, 1], repeat = n):
            for p in it.permutations(np.arange(n)):
                if np.product(i) * p_parity(p) == 1:
                    s = [slice(None, None, j) for j in i]
                    yield np.transpose(m[s], p)    
    

    This works for any (even non-square) tensors of arbitrary dimension and is based directly on the definition of regular rotations under tensor algebra below.

    Background

    Easiest way to explain this is in tensor terms, so lets turn all those rotations into rotation tensors. Rotation tensors are n x n matrices that rotate an n-dimensional space. As such they have a few properties:

    np.linalg.det(R) == 1                    # determinant = 1
    np.inner(R, R.T) == np.eye(R.shape[0])   # Transpose is inverse
    

    In addition, for 90 degree roatations all terms must be either 0, 1, or -1.

    In three dimensions, there are three basic families of these, which compose togther to make your 24 rotations.

    The first is simple permutation:

    A = 
    [[[1, 0, 0],
      [0, 1, 0],
      [0, 0, 1]],
    
     [[0, 1, 0],
      [0, 0, 1],
      [1, 0, 0]],
    
     [[0, 0, 1],
      [1, 0, 0],
      [0, 1, 0]]]
    

    The second involves negating some terms so that the product of the diagonal is always 1:

    B = 
    [[[ 1, 0, 0],
      [ 0, 1, 0],
      [ 0, 0, 1]],
    
     [[-1, 0, 0],
      [ 0,-1, 0],
      [ 0, 0, 1]],
    
     [[-1, 0, 0],
      [ 0, 1, 0],
      [ 0, 0,-1]],
    
     [[ 1, 0, 0],
      [ 0,-1, 0],
      [ 0, 0,-1]]]
    

    And the third determines whether the permutation is positive or negative, and negates the terms if negative

    C = 
    [[[ 1, 0, 0],
      [ 0, 1, 0],
      [ 0, 0, 1]],
    
     [[ 0, 0,-1],
      [ 0,-1, 0],
      [-1, 0, 0]],
    

    The imprtant thing about these families is in each family any product, power or transpose of two matrices yields another matrix in the family. Since we have three families, their products with each other form all the possible rotations, in this case 3*4*2 = 24

    Note: the other 24 "irregular" rotations are the same matrices multiplied by -np.eye(3) which yeild similar matrices with determinant = -1

    Application

    That's all well and good, but how does that relate to array manipulation? We don't want to rotate by matrix multiplication, as that will cause undue overhead in memory and processing. Luckily, each family is easily related to a an array manipulation that produces a view.

    def A_(m, i):  # i in (0, 1, 2)
        idx = np.array([[0, 1, 2], [1, 2, 0], [2, 0, 1]])
        return np.transpose(m, idx[i])
    
    def B_(m, j):  # j in (0, 1, 2, 3)
        idx = np.array([[ 1, 1, 1],
                        [ 1,-1,-1],
                        [-1, 1,-1],
                        [-1,-1, 1]])
        return m[::idx[j, 0], ::idx[j, 1], ::idx[j, 2]]
    
    def C_(m, k):  # k in (1, -1)
        return np.transpose(m, np.arange(3)[::k])[::k, ::k, ::k]
    

    All of these produce views of m, and you can create a generator that produces views relating to all of the rotations by:

    def cube_rot_gen(m):
        for i in [0, 1, 2]:
            for j in [0, 1, 2, 3]:
                for k in [1, -1]:
                    yield C_(B_(A_(m, i), j), k)
    

提交回复
热议问题