def write_ply(file_path: str, points: np.ndarray, normals=None, colors=None):
    """
    Write point cloud file as .ply.
    :param file_path:
    :param points:
    :param normals:
    :param colors:
    :return: None
    """

    import trimesh

    assert(file_path.endswith('.ply'))

    file_utils.make_dir_for_file(file_path)

    if points.shape == (3,):
        points = np.expand_dims(points, axis=0)

    if points.shape[0] == 3 and points.shape[1] != 3:
        points = points.transpose([1, 0])

    if colors is not None and colors.shape[0] == 3 and colors.shape[1] != 3:
        colors = colors.transpose([1, 0])

    if normals is not None and normals.shape[0] == 3 and normals.shape[1] != 3:
        normals = normals.transpose([1, 0])

    # convert 2d points to 3d
    if points.shape[1] == 2:
        vertices_2p5d = np.zeros((points.shape[0], 3))
        vertices_2p5d[:, :2] = points
        vertices_2p5d[:, 2] = 0.0
        points = vertices_2p5d

    mesh = trimesh.Trimesh(vertices=points, vertex_colors=colors, vertex_normals=normals)
    mesh.export(file_path)

    Write point cloud file as .ply.
    :param file_path:
    :param points:
    :param normals:
    :param colors:
    :return: None
    
source/base/point_cloud.py