def PushCommandsLeft(
         self,
         text,  # text containing affine transformation commands
         # The next two arguments are optional:
     src_loc=OSrcLoc(),  # for debugging
         xcm=None):  # position of center of object
     self.PushLeft(AffineStack.CommandsToMatrix(text, src_loc, xcm))
    def PushCommandsRight(self,
                          text,  # text containing affine transformation commands
                          # The next two arguments are optional:
                          src_loc=OSrcLoc(),   # for debugging
                          xcm=None):  # position of center of object
        """Generate affine transformation matrices from simple text commands
           (such as \"rotcm(90,0,0,1)\" and \"move(0,5.0,0)".
            Chains of "rotcm", "movecm", "rot", and "move" commands
            can also be strung together:
               \"rotcm(90,0,0,1).move(0,5.0,0)\"
           Commands ending in \"cm\" are carried out relative to center-of-mass
           (average position) of the object, and consequently require
           an additional argument (\"xcm\").

           """
        self.PushRight(AffineStack.CommandsToMatrix(text, src_loc, xcm))
    def CommandsToMatrix(text,  # text containing affine transformation commands
                         src_loc=OSrcLoc(),   # for debugging
                         xcm=None):  # position of center of object
        Mdest = [[1.0, 0.0, 0.0, 0.0], [
            0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]
        M = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]
        Mtmp = [[1.0, 0.0, 0.0, 0.0], [
            0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]

        transform_commands = text.split(').')
        for transform_str in transform_commands:
            if transform_str.find('move(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[5:i_paren_close].split(',')
                if (len(args) != 3):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires 3 numerical arguments.')
                M = [[1.0, 0.0, 0.0, float(args[0])],
                     [0.0, 1.0, 0.0, float(args[1])],
                     [0.0, 0.0, 1.0, float(args[2])]]
                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)

            # if transform_str.find('movecm(') == 0:
            # #     assert(xcm != None)
            #    i_paren_close = transform_str.find(')')
            #    if i_paren_close == -1:
            #        i_paren_close = len(transform_str)
            #    args = transform_str[8:i_paren_close].split(',')
            #    if (len(args) != 3):
            #        raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            #                         '       Invalid command: \"'+transform_str+'\"\n'
            #                         '       This command requires 3 numerical arguments.')
            #    M = [[1.0, 0.0, 0.0, float(args[0])-(xcm[0])],
            #         [0.0, 1.0, 0.0, float(args[1])-(xcm[1])],
            #         [0.0, 0.0, 1.0, float(args[2])-(xcm[2])]]
            #    AffineCompose(Mtmp, M, Mdest)
            #    CopyMat(Mdest, Mtmp)

            elif transform_str.find('move_rand(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[10:i_paren_close].split(',')

                seed = 1
                if len(args) in (2,4,7):
                    seed = int(args[0])
                random.seed(seed)
                if len(args) == 1:
                    sigma = float(args[1])
                    x = random.gauss(0.0, sigma)
                    y = random.gauss(0.0, sigma)
                    z = random.gauss(0.0, sigma)
                elif len(args) == 2:
                    # seed = int(args[0])  this was already handled above
                    sigma = float(args[1])
                    x = random.gauss(0.0, sigma)
                    y = random.gauss(0.0, sigma)
                    z = random.gauss(0.0, sigma)
                elif len(args) == 3:
                    x = random.gauss(0.0, float(args[0]))
                    y = random.gauss(0.0, float(args[1]))
                    z = random.gauss(0.0, float(args[2]))
                elif len(args) == 4:
                    # seed = int(args[0])   this was already handled above
                    x = random.gauss(0.0, float(args[1]))
                    y = random.gauss(0.0, float(args[2]))
                    z = random.gauss(0.0, float(args[3]))
                elif len(args) == 6:
                    x_min = float(args[0])
                    x_max = float(args[1])
                    y_min = float(args[2])
                    y_max = float(args[3])
                    z_min = float(args[4])
                    z_max = float(args[5])
                    x = x_min + (x_max - x_min)*(random.random()-0.5)
                    y = y_min + (y_max - y_min)*(random.random()-0.5)
                    z = z_min + (z_max - z_min)*(random.random()-0.5)
                        
                elif len(args) == 7:
                    # seed = int(args[0])  this was already handled above
                    x_min = float(args[1])
                    x_max = float(args[2])
                    y_min = float(args[3])
                    y_max = float(args[4])
                    z_min = float(args[5])
                    z_max = float(args[6])
                    x = x_min + (x_max - x_min)*(random.random()-0.5)
                    y = y_min + (y_max - y_min)*(random.random()-0.5)
                    z = z_min + (z_max - z_min)*(random.random()-0.5)
                else:
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 1, 2, 3, 4, 6 or 7 numerical arguments.  Either:\n'
                                     '           move_rand(gauss_sigma) or\n'
                                     '           move_rand(seed, gauss_sigma) or\n'
                                     '           move_rand(gauss_sigma_x, gauss_sigma_y, gauss_sigma_z) or\n'
                                     '           move_rand(seed, gauss_sigma_x, gauss_sigma_y, gauss_sigma_z) or\n'
                                     '           move_rand(x_min, x_max, y_min, y_max, z_min, z_max) or\n'
                                     '           move_rand(seed, x_min, x_max, y_min, y_max, z_min, z_max)\n')

                M = [[1.0, 0.0, 0.0, x],
                     [0.0, 1.0, 0.0, y],
                     [0.0, 0.0, 1.0, z]]
                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)

            elif transform_str.find('rot(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[4:i_paren_close].split(',')
                center_v = None
                if (len(args) == 7):
                    center_v = [float(args[4]), float(args[5]), float(args[6])]
                elif (len(args) != 4):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 4 or 7 numerical arguments.  Either:\n'
                                     '           rot(angle, axisX, axisY, axiZ)  or \n'
                                     '           rot(angle, axisX, axisY, axiZ, centerX, centerY, centerZ)')
                M[0][3] = 0.0  # RotMatAXYZ() only modifies 3x3 submatrix of M
                M[1][3] = 0.0  # The remaining final column must be zeroed by hand
                M[2][3] = 0.0
                RotMatAXYZ(M,
                           float(args[0]) * math.pi / 180.0,
                           float(args[1]),
                           float(args[2]),
                           float(args[3]))
                if (center_v == None):
                    AffineCompose(Mtmp, M, Mdest)
                    CopyMat(Mdest, Mtmp)
                else:
                    # Move "center_v" to the origin
                    moveCentToOrig = [[1.0, 0.0, 0.0, -center_v[0]],
                                      [0.0, 1.0, 0.0, -center_v[1]],
                                      [0.0, 0.0, 1.0, -center_v[2]]]
                    AffineCompose(Mtmp, moveCentToOrig, Mdest)
                    CopyMat(Mdest, Mtmp)
                    # Rotate the coordinates (relative to the origin)
                    AffineCompose(Mtmp, M, Mdest)  # M is the rotation matrix
                    CopyMat(Mdest, Mtmp)
                    # Move the origin back to center_v
                    moveCentBack = [[1.0, 0.0, 0.0, center_v[0]],
                                    [0.0, 1.0, 0.0, center_v[1]],
                                    [0.0, 0.0, 1.0, center_v[2]]]
                    AffineCompose(Mtmp, moveCentBack, Mdest)
                    CopyMat(Mdest, Mtmp)

            # # elif transform_str.find('rotcm(') == 0:
            # #     assert(xcm != None)
            # #     i_paren_close = transform_str.find(')')
            # #     if i_paren_close == -1:
            # #         i_paren_close = len(transform_str)
            # #     args = transform_str[6:i_paren_close].split(',')
            # #     if (len(args) != 4):
            # #         raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            # #                          '       Invalid command: \"'+transform_str+'\"\n'
            # #                          '       This command requires 4 numerical arguments.')
            # #
            # #     moveCMtoOrig = [[1.0, 0.0, 0.0, -xcm[0]],
            # #                     [0.0, 1.0, 0.0, -xcm[1]],
            # #                     [0.0, 0.0, 1.0, -xcm[2]]]
            # #     AffineCompose(Mtmp, moveCMtoOrig, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     M[0][3] = 0.0#RotMatAXYZ() only modifies 3x3 submatrix of M
            # #     M[1][3] = 0.0#The remaining final column must be zeroed by hand
            # #     M[2][3] = 0.0
            # #     RotMatAXYZ(M,
            # #                float(args[0])*math.pi/180.0,
            # #                float(args[1]),
            # #                float(args[2]),
            # #                float(args[3]))
            # #     AffineCompose(Mtmp, M, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     moveCmBack = [[1.0, 0.0, 0.0, xcm[0]],
            # #                   [0.0, 1.0, 0.0, xcm[1]],
            # #                   [0.0, 0.0, 1.0, xcm[2]]]
            # #     AffineCompose(Mtmp, moveCmBack, Mdest)
            # #     CopyMat(Mdest, Mtmp)

            elif transform_str.find('rot_rand(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[9:i_paren_close].split(',')

                seed = 1
                if len(args) in (2,6):
                    seed = int(args[0])
                random.seed(seed)
                raxis = [0.0, 0.0, 0.0]
                if len(args) < 5:
                    # choose a random rotation axis
                    raxis_len = 0.0
                    while (not ((0.01<raxis_len) and (raxis_len <= 1.0))):
                        raxis = [-1+2.0*(random.random()-0.5) for d in range(0,3)]
                        raxis_len = math.sqrt(raxis[0]**2 + raxis[1]**2 + raxis[2]**2)
                    for d in range(0,3):
                        raxis[d] /= raxis_len
                if len(args) == 0:
                    angle_min = angle_max = 2*math.pi
                elif len(args) == 1:
                    angle_min = 0.0
                    angle_max = float(args[0]) * math.pi / 180.0,
                elif len(args) == 5:
                    angle_min = float(args[0])
                    angle_max = float(args[1])
                    raxis[0]  = float(args[2])
                    raxis[1]  = float(args[3])
                    raxis[2]  = float(args[4])
                elif len(args) == 6:
                    seed      = int(args[0])
                    angle_min = float(args[1])
                    angle_max = float(args[2])
                    raxis[0]  = float(args[3])
                    raxis[1]  = float(args[4])
                    raxis[2]  = float(args[5])
                else:
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 0, 1, 2, 5 or 6 numerical arguments. Either:\n'
                                     '           rot_rand()  or \n'
                                     '           rot_rand(delta_angle)  or \n'
                                     '           rot_rand(seed, delta_angle)  or \n'
                                     '           rot_rand(angle_min, angle_max, axisX, axisY, axiZ) or\n'
                                     '           rot_rand(seed, angle_min, angle_max, axisX, axisY, axiZ)')

                angle = angle_min + (angle_max - angle_min)*(random.random() - 0.5)

                M[0][3] = 0.0  # RotMatAXYZ() only modifies 3x3 submatrix of M
                M[1][3] = 0.0  # The remaining final column must be zeroed by hand
                M[2][3] = 0.0
                RotMatAXYZ(M,
                           angle,
                           raxis[0], raxis[1], raxis[2])
                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)


            elif transform_str.find('rotvv(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[6:i_paren_close].split(',')
                center_v = None
                if (len(args) == 9):
                    center_v = [float(args[6]), float(args[7]), float(args[8])]
                elif (len(args) != 6):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 6 or 9 numerical arguments.  Either:\n'
                                     '           rotvv(Xold,Yold,Zold,Xnew,Ynew,Znew)  or \n'
                                     '           rotvv(Xold,Yold,Zold,Xnew,Ynew,Znew,centerX,centerY,centerZ)')
                M[0][3] = 0.0  # RotMatXYZXYZ() only modifies 3x3 submatrix of M
                M[1][3] = 0.0  # The remaining final column must be zeroed by hand
                M[2][3] = 0.0
                RotMatXYZXYZ(M,
                             float(args[0]),
                             float(args[1]),
                             float(args[2]),
                             float(args[3]),
                             float(args[4]),
                             float(args[5]))
                if (center_v == None):
                    AffineCompose(Mtmp, M, Mdest)
                    CopyMat(Mdest, Mtmp)
                else:
                    # Move "center_v" to the origin
                    moveCentToOrig = [[1.0, 0.0, 0.0, -center_v[0]],
                                      [0.0, 1.0, 0.0, -center_v[1]],
                                      [0.0, 0.0, 1.0, -center_v[2]]]
                    AffineCompose(Mtmp, moveCentToOrig, Mdest)
                    CopyMat(Mdest, Mtmp)
                    # Rotate the coordinates (relative to the origin)
                    AffineCompose(Mtmp, M, Mdest)  # M is the rotation matrix
                    CopyMat(Mdest, Mtmp)
                    # Move the origin back to center_v
                    moveCentBack = [[1.0, 0.0, 0.0, center_v[0]],
                                    [0.0, 1.0, 0.0, center_v[1]],
                                    [0.0, 0.0, 1.0, center_v[2]]]
                    AffineCompose(Mtmp, moveCentBack, Mdest)
                    CopyMat(Mdest, Mtmp)

            elif transform_str.find('scale(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[6:i_paren_close].split(',')

                if (len(args) == 1):
                    scale_v = [float(args[0]), float(args[0]), float(args[0])]
                    center_v = [0.0, 0.0, 0.0]
                elif (len(args) == 3):
                    scale_v = [float(args[0]), float(args[1]), float(args[2])]
                    center_v = [0.0, 0.0, 0.0]
                elif (len(args) == 4):
                    scale_v = [float(args[0]), float(args[0]), float(args[0])]
                    center_v = [float(args[1]), float(args[2]), float(args[3])]
                elif (len(args) == 6):
                    scale_v = [float(args[0]), float(args[1]), float(args[2])]
                    center_v = [float(args[3]), float(args[4]), float(args[5])]
                else:
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 1, 3, 4, or 6 numerical arguments. Either:\n'
                                     '           scale(ratio), or \n'
                                     '           scale(ratioX, ratioY, ratioZ),\n'
                                     '           scale(ratio, centerX, centerY, centerZ), or\n'
                                     '           scale(ratioX, ratioY, ratioZ, centerX, centerY, centerZ)')

                ScaleMat(M, scale_v)

                # Now worry about translation:
                for d in range(0, 3):
                    M[d][3] = center_v[d] * (1.0 - scale_v[d])

                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)

            # # elif transform_str.find('scalecm(') == 0:
            # #     assert(xcm != None)
            # #     i_paren_close = transform_str.find(')')
            # #     if i_paren_close == -1:
            # #         i_paren_close = len(transform_str)
            # #     args = transform_str[8:i_paren_close].split(',')
            # #
            # #     moveCMtoOrig = [[1.0, 0.0, 0.0, -xcm[0]],
            # #                     [0.0, 1.0, 0.0, -xcm[1]],
            # #                     [0.0, 0.0, 1.0, -xcm[2]]]
            # #     AffineCompose(Mtmp, moveCMtoOrig, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #
            # #     M[0][3] = 0.0 #ScaleMat() only modifies 3x3 submatrix of M
            # #     M[1][3] = 0.0 #The remaining final column must be zeroed by hand
            # #     M[2][3] = 0.0
            # #     if (len(args) == 1):
            # #         ScaleMat(M, args[0])
            # #     elif (len(args) == 3):
            # #         ScaleMat(M, args)
            # #     else:
            # #         raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            # #                          '       Invalid command: \"'+transform_str+'\"\n'
            # #                          '       This command requires either 1 or 3 numerical arguments.')
            # #
            # #     AffineCompose(Mtmp, M, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     moveCmBack = [[1.0, 0.0, 0.0, xcm[0]],
            # #                   [0.0, 1.0, 0.0, xcm[1]],
            # #                   [0.0, 0.0, 1.0, xcm[2]]]
            # #     AffineCompose(Mtmp, moveCmBack, Mdest)
            # #     CopyMat(Mdest, Mtmp)

            else:
                raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                 '       Unknown transformation command: \"' + transform_str + '\"\n')

        return Mdest
Beispiel #4
0
    def CommandsToMatrix(text,  # text containing affine transformation commands
                         src_loc=OSrcLoc(),   # for debugging
                         xcm=None):  # position of center of object
        Mdest = [[1.0, 0.0, 0.0, 0.0], [
            0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]
        M = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]
        Mtmp = [[1.0, 0.0, 0.0, 0.0], [
            0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]

        transform_commands = text.split(').')
        for transform_str in transform_commands:
            if transform_str.find('move(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[5:i_paren_close].split(',')
                if (len(args) != 3):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires 3 numerical arguments.')
                M = [[1.0, 0.0, 0.0, float(args[0])],
                     [0.0, 1.0, 0.0, float(args[1])],
                     [0.0, 0.0, 1.0, float(args[2])]]
                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)

            # if transform_str.find('movecm(') == 0:
            # #     assert(xcm != None)
            #    i_paren_close = transform_str.find(')')
            #    if i_paren_close == -1:
            #        i_paren_close = len(transform_str)
            #    args = transform_str[8:i_paren_close].split(',')
            #    if (len(args) != 3):
            #        raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            #                         '       Invalid command: \"'+transform_str+'\"\n'
            #                         '       This command requires 3 numerical arguments.')
            #    M = [[1.0, 0.0, 0.0, float(args[0])-(xcm[0])],
            #         [0.0, 1.0, 0.0, float(args[1])-(xcm[1])],
            #         [0.0, 0.0, 1.0, float(args[2])-(xcm[2])]]
            #    AffineCompose(Mtmp, M, Mdest)
            #    CopyMat(Mdest, Mtmp)

            elif transform_str.find('rot(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[4:i_paren_close].split(',')
                center_v = None
                if (len(args) == 7):
                    center_v = [float(args[4]), float(args[5]), float(args[6])]
                elif (len(args) != 4):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 4 or 7 numerical arguments.  Either:\n'
                                     '           rot(angle, axisX, axisY, axiZ)  or \n'
                                     '           rot(angle, axisX, axisY, axiZ, centerX, centerY, centerZ)')
                M[0][3] = 0.0  # RotMatAXYZ() only modifies 3x3 submatrix of M
                M[1][3] = 0.0  # The remaining final column must be zeroed by hand
                M[2][3] = 0.0
                RotMatAXYZ(M,
                           float(args[0]) * math.pi / 180.0,
                           float(args[1]),
                           float(args[2]),
                           float(args[3]))
                if (center_v == None):
                    AffineCompose(Mtmp, M, Mdest)
                    CopyMat(Mdest, Mtmp)
                else:
                    # Move "center_v" to the origin
                    moveCentToOrig = [[1.0, 0.0, 0.0, -center_v[0]],
                                      [0.0, 1.0, 0.0, -center_v[1]],
                                      [0.0, 0.0, 1.0, -center_v[2]]]
                    AffineCompose(Mtmp, moveCentToOrig, Mdest)
                    CopyMat(Mdest, Mtmp)
                    # Rotate the coordinates (relative to the origin)
                    AffineCompose(Mtmp, M, Mdest)  # M is the rotation matrix
                    CopyMat(Mdest, Mtmp)
                    # Move the origin back to center_v
                    moveCentBack = [[1.0, 0.0, 0.0, center_v[0]],
                                    [0.0, 1.0, 0.0, center_v[1]],
                                    [0.0, 0.0, 1.0, center_v[2]]]
                    AffineCompose(Mtmp, moveCentBack, Mdest)
                    CopyMat(Mdest, Mtmp)

            # # elif transform_str.find('rotcm(') == 0:
            # #     assert(xcm != None)
            # #     i_paren_close = transform_str.find(')')
            # #     if i_paren_close == -1:
            # #         i_paren_close = len(transform_str)
            # #     args = transform_str[6:i_paren_close].split(',')
            # #     if (len(args) != 4):
            # #         raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            # #                          '       Invalid command: \"'+transform_str+'\"\n'
            # #                          '       This command requires 4 numerical arguments.')
            # #
            # #     moveCMtoOrig = [[1.0, 0.0, 0.0, -xcm[0]],
            # #                     [0.0, 1.0, 0.0, -xcm[1]],
            # #                     [0.0, 0.0, 1.0, -xcm[2]]]
            # #     AffineCompose(Mtmp, moveCMtoOrig, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     M[0][3] = 0.0#RotMatAXYZ() only modifies 3x3 submatrix of M
            # #     M[1][3] = 0.0#The remaining final column must be zeroed by hand
            # #     M[2][3] = 0.0
            # #     RotMatAXYZ(M,
            # #                float(args[0])*math.pi/180.0,
            # #                float(args[1]),
            # #                float(args[2]),
            # #                float(args[3]))
            # #     AffineCompose(Mtmp, M, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     moveCmBack = [[1.0, 0.0, 0.0, xcm[0]],
            # #                   [0.0, 1.0, 0.0, xcm[1]],
            # #                   [0.0, 0.0, 1.0, xcm[2]]]
            # #     AffineCompose(Mtmp, moveCmBack, Mdest)
            # #     CopyMat(Mdest, Mtmp)

            elif transform_str.find('rotvv(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[6:i_paren_close].split(',')
                center_v = None
                if (len(args) == 9):
                    center_v = [float(args[6]), float(args[7]), float(args[8])]
                elif (len(args) != 6):
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 6 or 9 numerical arguments.  Either:\n'
                                     '           rotvv(Xold,Yold,Zold,Xnew,Ynew,Znew)  or \n'
                                     '           rotvv(Xold,Yold,Zold,Xnew,Ynew,Znew,centerX,centerY,centerZ)')
                M[0][3] = 0.0  # RotMatXYZXYZ() only modifies 3x3 submatrix of M
                M[1][3] = 0.0  # The remaining final column must be zeroed by hand
                M[2][3] = 0.0
                RotMatXYZXYZ(M,
                             float(args[0]),
                             float(args[1]),
                             float(args[2]),
                             float(args[3]),
                             float(args[4]),
                             float(args[5]))
                if (center_v == None):
                    AffineCompose(Mtmp, M, Mdest)
                    CopyMat(Mdest, Mtmp)
                else:
                    # Move "center_v" to the origin
                    moveCentToOrig = [[1.0, 0.0, 0.0, -center_v[0]],
                                      [0.0, 1.0, 0.0, -center_v[1]],
                                      [0.0, 0.0, 1.0, -center_v[2]]]
                    AffineCompose(Mtmp, moveCentToOrig, Mdest)
                    CopyMat(Mdest, Mtmp)
                    # Rotate the coordinates (relative to the origin)
                    AffineCompose(Mtmp, M, Mdest)  # M is the rotation matrix
                    CopyMat(Mdest, Mtmp)
                    # Move the origin back to center_v
                    moveCentBack = [[1.0, 0.0, 0.0, center_v[0]],
                                    [0.0, 1.0, 0.0, center_v[1]],
                                    [0.0, 0.0, 1.0, center_v[2]]]
                    AffineCompose(Mtmp, moveCentBack, Mdest)
                    CopyMat(Mdest, Mtmp)

            elif transform_str.find('scale(') == 0:
                i_paren_close = transform_str.find(')')
                if i_paren_close == -1:
                    i_paren_close = len(transform_str)
                args = transform_str[6:i_paren_close].split(',')

                if (len(args) == 1):
                    scale_v = [float(args[0]), float(args[0]), float(args[0])]
                    center_v = [0.0, 0.0, 0.0]
                elif (len(args) == 3):
                    scale_v = [float(args[0]), float(args[1]), float(args[2])]
                    center_v = [0.0, 0.0, 0.0]
                elif (len(args) == 4):
                    scale_v = [float(args[0]), float(args[0]), float(args[0])]
                    center_v = [float(args[1]), float(args[2]), float(args[3])]
                elif (len(args) == 6):
                    scale_v = [float(args[0]), float(args[1]), float(args[2])]
                    center_v = [float(args[3]), float(args[4]), float(args[5])]
                else:
                    raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                     '       Invalid command: \"' + transform_str + '\"\n'
                                     '       This command requires either 1, 3, 4, or 6 numerical arguments. Either:\n'
                                     '           scale(ratio), or \n'
                                     '           scale(ratioX, ratioY, ratioZ),\n'
                                     '           scale(ratio, centerX, centerY, centerZ), or\n'
                                     '           scale(ratioX, ratioY, ratioZ, centerX, centerY, centerZ)')

                ScaleMat(M, scale_v)

                # Now worry about translation:
                for d in range(0, 3):
                    M[d][3] = center_v[d] * (1.0 - scale_v[d])

                AffineCompose(Mtmp, M, Mdest)
                CopyMat(Mdest, Mtmp)

            # # elif transform_str.find('scalecm(') == 0:
            # #     assert(xcm != None)
            # #     i_paren_close = transform_str.find(')')
            # #     if i_paren_close == -1:
            # #         i_paren_close = len(transform_str)
            # #     args = transform_str[8:i_paren_close].split(',')
            # #
            # #     moveCMtoOrig = [[1.0, 0.0, 0.0, -xcm[0]],
            # #                     [0.0, 1.0, 0.0, -xcm[1]],
            # #                     [0.0, 0.0, 1.0, -xcm[2]]]
            # #     AffineCompose(Mtmp, moveCMtoOrig, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #
            # #     M[0][3] = 0.0 #ScaleMat() only modifies 3x3 submatrix of M
            # #     M[1][3] = 0.0 #The remaining final column must be zeroed by hand
            # #     M[2][3] = 0.0
            # #     if (len(args) == 1):
            # #         ScaleMat(M, args[0])
            # #     elif (len(args) == 3):
            # #         ScaleMat(M, args)
            # #     else:
            # #         raise InputError('Error near '+ErrorLeader(src_loc.infile, src_loc.lineno)+':\n'
            # #                          '       Invalid command: \"'+transform_str+'\"\n'
            # #                          '       This command requires either 1 or 3 numerical arguments.')
            # #
            # #     AffineCompose(Mtmp, M, Mdest)
            # #     CopyMat(Mdest, Mtmp)
            # #     moveCmBack = [[1.0, 0.0, 0.0, xcm[0]],
            # #                   [0.0, 1.0, 0.0, xcm[1]],
            # #                   [0.0, 0.0, 1.0, xcm[2]]]
            # #     AffineCompose(Mtmp, moveCmBack, Mdest)
            # #     CopyMat(Mdest, Mtmp)

            else:
                raise InputError('Error near ' + ErrorLeader(src_loc.infile, src_loc.lineno) + ':\n'
                                 '       Unknown transformation command: \"' + transform_str + '\"\n')

        return Mdest