How to find a mesh loops and rings I'm trying to find a clear, safe, simple explanation to the ring and loop madness. I select an edge:



Then I run this script:

import bpy
import bmesh

# Walk rings from currently selected edges.
# Run this in edit mode!

def find_edge_loops(loop,max_loops=1000):
    i=0
    first_loop=loop
    while i&lt;max_loops: 
        # Jump to adjacent face and walk two edges forward
        loop = loop.link_loop_next.link_loop_radial_next.link_loop_next
        loop.edge.select = True
        i += 1
        # If radial loop links back here, we're boundary, thus done        
        if loop == first_loop:
            break        

bm = bmesh.from_edit_mesh(bpy.context.object.data)
selected_edges = [ e for e in bm.edges if e.select ]

for edge in selected_edges:
    # Get rings from "forward" loop
    find_edge_loops(edge.link_loops[0])
    # Get rings from "backward" loop
    # rings(edge.link_loops[1])

bm.select_flush_mode()
bpy.context.object.data.update()


(It's equivalent to select edge loops in select menu)
And I get, correctly:



Then, again, I select the starting edge, and I run this other script:

import bpy
import bmesh

# Walk rings from currently selected edges.
# Run this in edit mode!

def find_edge_rings(loop,max_loops=1000):
    i=0
    first_loop=loop
    while i&lt;max_loops: 
        # Jump to adjacent face and walk two edges forward
        loop = loop.link_loop_radial_next.link_loop_next.link_loop_next
        loop.edge.select = True
        i += 1
        # If radial loop links back here, we're boundary, thus done        
        if loop == first_loop:
            break        

bm = bmesh.from_edit_mesh(bpy.context.object.data)
selected_edges = [ e for e in bm.edges if e.select ]

for edge in selected_edges:
    # Get rings from "forward" loop
    find_edge_rings(edge.link_loops[0])
    # Get rings from "backward" loop
    # rings(edge.link_loops[1])

bm.select_flush_mode()
bpy.context.object.data.update()


(It's equivalent to select edge rings in select menu) and I get:



I summarized those scripts from sources here and there, and I see they work. But, for the life of me, I can't understand the precise relation which stems from:

loop.link_loop_next.link_loop_radial_next.link_loop_next (change loop)
loop.link_loop_radial_next.link_loop_next.link_loop_next (change ring)


Can someone clarify the way loops and rings are traversed, possibly with a picture? The api is obscure, I didn't find any official code examples, and I'd like to have a much clearer idea of what's suggested in the vague bmesh design documents
The image below will be used as a reference. For clarity's sake, only the normal for face 12 is displayed. The normal is the arrow pointing away from the face, the other two arrows indicate the first edge/vertex (the starting point) and the "spin" (order) of the vertices and edges. For example, this is the vertex and edge ordering (from first to last) for the selected face (12) in the image below:


vert order: 17, 24, 25, 18
edge order: 58, 24, 59, 17




base_edge.link_loops[base_loop].etc


link loop = a loop representing all edges that are part of a face. these edges are sorted counter clockwise around the face normal
link_loops = every "link loop" an edge is part of (edge 24 has 2 loops, edge 67 has 1)
link_loop_next = the next edge in a loop (if face 12's loop was the base, the next edge after 24 would be 59)
link_loop_radial_next = another "link loop" an edge is part of. (if the base loop was for face 12, calling radial next on edge 24 would return the loop for face 19). this returns the same value that is stored in the next loop of an edge's link_loops property (if link_loops[0] was for face 12, link_loops[1] would be for 19).