def RotatePerturbation(points,v):
    assert 0 <= v <= 10
    v = int(v)
    angle_sigma = 0.1 * v
    angle_clip = 0.1 * v
    n_idx = 50 * v

    angles = np.clip(angle_sigma * np.random.randn(3), -angle_clip, angle_clip)
    Rx = angle_axis(angles[0], np.array([1.0, 0.0, 0.0]))
    Ry = angle_axis(angles[1], np.array([0.0, 1.0, 0.0]))
    Rz = angle_axis(angles[2], np.array([0.0, 0.0, 1.0]))

    rotation_matrix = Rz @ Ry @ Rx

    center = torch.mean(points[:,0:3], dim = 0)
    idx = np.random.choice(points.size(0),n_idx)

    perturbation = points[idx, 0:3] - center
    points[idx, :3] += (perturbation @ rotation_matrix.t()) - perturbation

    normals = points.size(1) > 3
    if normals:
        pc_normals = points[idx, 3:]
        points[idx, 3:] = pc_normals @ rotation_matrix.t()

    return points
def RandomAxisRotation(points,v):
    assert 0 <= v <= 2 * np.pi
    axis = np.random.randn(3)
    axis /= np.sqrt((axis**2).sum())

    rotation_angle = np.random.uniform() * v
    rotation_matrix = angle_axis(rotation_angle, axis)

    normals = points.size(1) > 3
    if not normals:
        return points @ rotation_matrix.t()
    else:
        pc_xyz = points[:, 0:3]
        pc_normals = points[:, 3:]
        points[:, 0:3] = pc_xyz @ rotation_matrix.t()
        points[:, 3:] = pc_normals @ rotation_matrix.t()

        return points
def RotateY(points,v): # ( 0 , 2 * pi)
    assert 0 <= v <= 2 * np.pi
    if np.random.random() > 0.5:
        v *= -1
    axis = np.array([0. , 1. , 0.])

    rotation_angle = np.random.uniform() * v
    rotation_matrix = angle_axis(rotation_angle, axis)

    normals = points.size(1) > 3
    if not normals:
        return points @ rotation_matrix.t()
    else:
        pc_xyz = points[:, 0:3]
        pc_normals = points[:, 3:]
        points[:, 0:3] = pc_xyz @ rotation_matrix.t()
        points[:, 3:] = pc_normals @ rotation_matrix.t()

        return points