Skip to content

boundary_loops

boundary_loops(f, allow_wrong_orientations=True)

Computes a list containing the oriented boundary loop for each boundary component of a triangle mesh in the style of a sorted polyline. This function only works on connected (i.e., single component) manifold triangle meshes.

Parameters:

Name Type Description Default
f (m,3) numpy int array

face index list of a triangle mesh

required
allow_wrong_orientations

whether to allow F to contain wrongly oriented triangles

True

Returns:

Name Type Description
loops list of numpy arrays that are themselves lists of boundary vertices

in oriented loops

Examples:

from gpytoolbox import read_mesh, boundary_loops
v,f = read_mesh("test/unit_tests_data/bunny_oded.obj")
loops = boundary_loops(f)
Source code in src/gpytoolbox/boundary_loops.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
def boundary_loops(f, allow_wrong_orientations=True):
    """Computes a list containing the oriented boundary loop for each boundary component of a triangle mesh in the style of a sorted polyline. This function only works on connected (i.e., single component) manifold triangle meshes.

    Parameters
    ----------
    f : (m,3) numpy int array
        face index list of a triangle mesh
    allow_wrong_orientations: bool, optional (default True).
        whether to allow F to contain wrongly oriented triangles

    Returns
    -------
    loops : list of numpy arrays that are themselves lists of boundary vertices
        in oriented loops

    Examples
    --------
    ```python
    from gpytoolbox import read_mesh, boundary_loops
    v,f = read_mesh("test/unit_tests_data/bunny_oded.obj")
    loops = boundary_loops(f)
    ```
    """

    assert f.shape[0] > 0
    assert f.shape[1] == 3

    # check mesh is manifold
    assert len(non_manifold_edges(f)) == 0, "Mesh is not manifold"

    bE = boundary_edges(f)

    #Loop through each boundary, edge, marking them as seen, until all have
    # been seen.
    unseen = np.full(bE.shape[0], True)
    loops = []
    while np.any(unseen):
        current_b = np.argmax(unseen)
        current_bE = bE[current_b,:]
        start = current_bE[0]
        unseen[current_b] = False

        loop_vertices = []
        loop_vertices.append(start)

        head = current_bE[1]
        while head != start:
            loop_vertices.append(head)

            if allow_wrong_orientations:
                current_b_0 = np.where((bE[:,0] == head) & unseen)[0]
                current_b_1 = np.where((bE[:,1] == head) & unseen)[0]
                if len(current_b_0)>len(current_b_1):
                    current_b = current_b_0
                    head_ind = 1
                else:
                    current_b = current_b_1
                    head_ind = 0
            else:
                current_b = np.where((bE[:,0] == head) & unseen)[0]
                head_ind = 1
            assert len(current_b) == 1
            current_b = current_b.item(0)
            current_bE = bE[current_b,:]
            unseen[current_b] = False
            head = current_bE[head_ind]

        loops.append(np.array(loop_vertices))

    assert sum([len(l) for l in loops]) == bE.shape[0]
    return loops