Example #1
0
def test_functions():
    """modify the config and verify that various functions correctly use the updated version
    """
    config = Configuration.config
    mesh = trimesh.load(config.mesh, validate=True)
    print()
    # BSPNode instantiation (n_parts)
    n_parts_1 = bsp_node.BSPNode(mesh).n_parts
    config.printer_extents = np.array([20, 20, 20])
    n_parts_2 = bsp_node.BSPNode(mesh).n_parts
    assert n_parts_1 != n_parts_2
    config.restore_defaults()

    # get_planes (plane_spacing, default is )
    node = bsp_node.BSPNode(mesh)
    planes_1 = bsp_tree.get_planes(node.part, np.array([0, 1, 0]))
    config.plane_spacing /= 2
    planes_2 = bsp_tree.get_planes(node.part, np.array([0, 1, 0]))
    assert len(planes_2) > len(planes_1)
    config.restore_defaults()

    # uniform normals
    normals1 = config.normals.copy()
    config.n_theta = 10
    normals2 = config.normals.copy()
    config.n_phi = 10
    normals3 = config.normals.copy()
    assert len(normals1) < len(normals2) < len(normals3)
    config.restore_defaults()
Example #2
0
def evaluate_cuts(base_tree, node):
    """this function returns a list of unique trees by splitting a specified node of an input tree along all planes
    as defined in the configuration

    :param base_tree: tree to split at a particular node
    :type base_tree: `bsp_tree.BSPTree`
    :param node: node of the input tree to split
    :type node: `bsp_node.BSPNode`
    :return: list of 'unique' trees resulting from splitting the input tree at the specified node
    :rtype: list of `bsp_tree.BSPTree`
    """
    config = Configuration.config  # collect configuration

    N = config.normals  # collect predefined set of normal vectors
    Np = node.auxiliary_normals  # add in the part's bounding-box-aligned vectors as normals
    N = utils.get_unique_normals(np.concatenate(
        (N, Np), axis=0))  # make sure we only have unique normals

    trees = []  # initial list of all trees resulting from splitting the node
    for i in range(N.shape[0]):
        trees_of_this_normal = [
        ]  # start a list of trees for splits along this normal
        normal = N[i]  # current normal
        for plane in bsp_tree.get_planes(
                node.part,
                normal):  # iterate over all valid cutting planes for the node
            tree, result = bsp_tree.expand_node(
                base_tree, node.path, plane)  # split the node using the plane
            if tree:  # only keep the tree if the split is successful
                trees_of_this_normal.append(tree)
            logger.debug(
                f"normal index: {i}, origin: {plane[0]}, normal: {plane[1]}, result: {result}"
            )
        if len(
                trees_of_this_normal
        ) == 0:  # avoid empty list errors during objective function evaluation
            logger.info(
                f"normal index: {i}, trees for normal: {len(trees_of_this_normal)}, total trees: {len(trees)}"
            )
            continue
        # go through each objective function, evaluate the objective function for each tree in this normal's
        # list, fill in the data in each tree object in the list
        for evaluate_objective_func in objectives.values():
            evaluate_objective_func(trees_of_this_normal, node.path)
        trees += trees_of_this_normal
        logger.info(
            f"normal index: {i}, trees for normal: {len(trees_of_this_normal)}, total trees: {len(trees)}"
        )

    # go through the list of trees, best ones first, and throw away any that are too similar to another tree already
    # in the result list
    result_set = []
    for tree in sorted(trees, key=lambda x: x.objective):
        if tree.sufficiently_different(node, result_set):
            result_set.append(tree)
    logger.info(f"{len(result_set)} valid trees")
    return result_set
Example #3
0
def test_expand_node():
    """no errors when using expand_node, need to think of better tests here"""
    config = Configuration.config
    mesh = trimesh.load(config.mesh, validate=True)

    # make tree, get node, get random normal, pick a plane right through middle, make sure that the slice is good
    tree = bsp_tree.BSPTree(mesh)

    node = tree.largest_part
    normal = np.array([0, 0, 1])
    planes = bsp_tree.get_planes(node.part, normal)
    plane = planes[len(planes) // 2]
    tree1 = bsp_tree.expand_node(tree, node.path, plane)
    print("tree objective: ", tree1.objective)

    node = tree1.largest_part
    planes = bsp_tree.get_planes(node.part, normal)
    plane = planes[len(planes) // 2]
    tree2 = bsp_tree.expand_node(tree1, node.path, plane)
Example #4
0
def test_get_planes(config):
    """verify that for the default bunny mesh, which is a single part, all planes returned by `bsp_tree.get_planes`
        cut through the mesh (they have a good cross section)
    """
    mesh = trimesh.load(config.mesh, validate=True)

    for i in range(100):
        normal = trimesh.unitize(np.random.rand(3))
        planes = bsp_tree.get_planes(mesh, normal)

        for origin, normal in planes:
            path3d = mesh.section(plane_origin=origin, plane_normal=normal)
            assert path3d is not None
Example #5
0
def test_copy_tree(config):
    """Now that objectives are calculated outside of the tree (using the objective function evaluators), verify
    that copying a tree doesn't modify its objectives dict
    """
    mesh = trimesh.load(config.mesh, validate=True)

    # make tree, get node, get random normal, pick a plane right through middle, make sure that the slice is good
    tree = bsp_tree.BSPTree(mesh)
    node = tree.largest_part
    normal = np.array([0, 0, 1])
    planes = bsp_tree.get_planes(node.part, normal)
    plane = planes[len(planes) // 2]
    tree, result = bsp_tree.expand_node(tree, node.path, plane)
    new_tree = tree.copy()
    assert new_tree.objectives == tree.objectives
Example #6
0
def test_different_from():
    """verify that `BSPNode.different_from` has the expected behavior

    Get a list of planes. Split the object using the first plane, then for each of the other planes, split the object,
    check if the plane is far enough away given the config, then assert that `BSPNode.different_from` returns the
    correct value. This skips any splits that fail.
    """
    config = Configuration.config
    print()
    mesh = trimesh.primitives.Sphere(radius=50)

    tree = bsp_tree.BSPTree(mesh)
    root = tree.nodes[0]
    normal = trimesh.unitize(np.random.rand(3))
    planes = bsp_tree.get_planes(mesh, normal)
    base_node = copy.deepcopy(root)
    base_node = bsp_node.split(base_node, planes[0])

    for plane in planes[1:]:
        # smaller origin offset, should not be different
        test_node = copy.deepcopy(root)
        test_node = bsp_node.split(test_node, plane)
        if abs((plane[0] - planes[0][0])
               @ planes[0][1]) > config.different_origin_th:
            assert base_node.different_from(test_node)
        else:
            assert not base_node.different_from(test_node)

    # smaller angle difference, should not be different
    test_node = copy.deepcopy(root)
    random_vector = trimesh.unitize(np.random.rand(3))
    axis = np.cross(random_vector, planes[0][1])
    rotation = trimesh.transformations.rotation_matrix(np.pi / 11, axis)
    normal = trimesh.transform_points(planes[0][1][None, :], rotation)[0]
    test_plane = (planes[0][0], normal)
    test_node = bsp_node.split(test_node, test_plane)
    assert not base_node.different_from(test_node)

    # larger angle difference, should be different
    test_node = copy.deepcopy(root)
    random_vector = trimesh.unitize(np.random.rand(3))
    axis = np.cross(random_vector, planes[0][1])
    rotation = trimesh.transformations.rotation_matrix(np.pi / 9, axis)
    normal = trimesh.transform_points(planes[0][1][None, :], rotation)[0]
    test_plane = (planes[0][0], normal)
    test_node = bsp_node.split(test_node, test_plane)
    assert base_node.different_from(test_node)
Example #7
0
def test_fragility_function_multiple_trees(config):
    config.plane_spacing = 5
    config.connector_diameter = 5
    mesh_fn = os.path.join(os.path.dirname(__file__), 'test_meshes', 'fragility_test_1.stl')
    mesh = trimesh.load(mesh_fn)
    mesh = mesh.subdivide()
    mesh = mesh.subdivide()

    tree = bsp_tree.BSPTree(mesh)
    normal = np.array([0., 0., 1.])
    planes = bsp_tree.get_planes(mesh, normal)
    trees = []
    for plane in planes:
        candidate, result = bsp_tree.expand_node(tree, tree.nodes[0].path, plane)
        trees.append(candidate)
    objective_functions.evaluate_fragility_objective(trees, tree.nodes[0].path)
    assert trees[0].objectives['fragility'] == np.inf
    assert trees[6].objectives['fragility'] == np.inf
    assert trees[7].objectives['fragility'] == np.inf
    assert trees[11].objectives['fragility'] == np.inf
Example #8
0
def process_normal(normal, node, base_tree, config):
    Configuration.config = config
    trees_of_this_normal = [
    ]  # start a list of trees for splits along this normal
    for plane in bsp_tree.get_planes(
            node.part,
            normal):  # iterate over all valid cutting planes for the node
        tree, result = bsp_tree.expand_node(
            base_tree, node.path, plane)  # split the node using the plane
        if tree:  # only keep the tree if the split is successful
            trees_of_this_normal.append(tree)
    if len(
            trees_of_this_normal
    ) == 0:  # avoid empty list errors during objective function evaluation
        return trees_of_this_normal
    # go through each objective function, evaluate the objective function for each tree in this normal's
    # list, fill in the data in each tree object in the list
    for evaluate_objective_func in objectives.values():
        evaluate_objective_func(trees_of_this_normal, node.path)
    print('.', end='')
    return trees_of_this_normal