"Turtle selection" nodes? ...
Created by: portnov
Problem statement
Sometimes you want to select faces by some more or less complex algorithm. For example, select each second face. Or select one and skip two following.
But the problem is, the mesh topology can be quite complex, so there is no easy way to formulate such algorithm for an arbitrary mesh.
Solution proposal
Provide a "Turtle-style" API for mesh traversing. At each point in time, the turtle is standing at one face of the mesh and looking towards one of the face edges. It can turn to the next or to the previous edge; it can jump to the face that is beyond that edge. The thing with arbitrary mesh is that we don't know how many edges the next face will have, so where exactly should turtle look at?... Proposed solution is: after click()
method, it will look at the same edge it looked before, but from another side. turn_opposite()
method will turn the turtle around. If number of face edges is odd, then "opposite" direction is ambiguous; so we will have "bias" preference: whether to tend to "n / 2"th or to "n / 2 + 1"th edge. step()
method is combination of click()
and turn_opposite()
.
Such API can be used in a scripted node. As an option, we can make a special kind of scripted node for selection purposes.
An example:
import bmesh
import mathutils
from sverchok.utils.logging import debug
PREVIOUS = 'PREVIOUS'
NEXT = 'NEXT'
class Turtle(object):
def __init__(self, bm):
self.bmesh = bm
self.current_face = bm.faces[0]
self.current_loop = self.current_face.loops[0]
self.opposite_bias = PREVIOUS
self.visited_faces = set()
def turn_next(self):
self.current_loop = self.current_loop.link_loop_next
def turn_prev(self):
self.current_loop = self.current_loop.link_loop_prev
def click(self):
self.visited_faces.add(self.current_face.index)
next_loop = self.current_loop.link_loop_radial_next
self.current_loop = next_loop
self.current_face = next_loop.face
debug("Current face # := %s", self.current_face.index)
def turn_opposite(self):
n = len(self.current_face.loops)
if n % 2 == 0:
steps = n // 2
else:
if self.opposite_bias == 'PREVIOUS':
steps = n // 2
else:
steps = n // 2 + 1
for i in range(steps):
self.turn_next()
def step(self, count=1):
for i in range(count):
self.click()
self.turn_opposite()
def select(self):
self.current_face.select = True
debug("Selecting face #%s", self.current_face.index)
def unselect(self):
self.current_face.select = False
debug("Unselecting face #%s", self.current_face.index)
def toggle(self):
self.current_face.select = not self.current_face.select
debug("Set face #%s selection := %s", self.current_face.select)
def get_selected_faces(self):
return [[vert.index for vert in face.verts] for face in self.bmesh.faces if face.select]
def get_selection_mask(self):
return [face.select for face in self.bmesh.faces]
@property
def is_looking_at_boundary(self):
return self.current_loop.edge.is_boundary
@property
def is_at_boundary(self):
return any(edge.is_boundary for edge in self.current_face.edges)
@property
def was_here(self):
return self.current_face.index in self.visited_faces
"""
in in_verts v
in in_faces s
out out_face_mask s
"""
from sverchok.data_structure import zip_long_repeat
from sverchok.utils.sv_bmesh_utils import bmesh_from_pydata
from sverchok.utils.turtle import Turtle
out_face_mask = []
objects = zip_long_repeat(in_verts, in_faces)
for verts, faces in objects:
bm = bmesh_from_pydata(verts, [], faces, normal_update=True)
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
turtle = Turtle(bm)
#while turtle.is_looking_at_boundary:
# turtle.turn_next()
turtle.turn_next()
while True:
# Select one face, step to the following and select it too
turtle.select()
turtle.step()
turtle.select()
# Make two steps in the same direction
turtle.step(2)
# Stop if we already were here - to prevent infinite loop
if turtle.was_here:
break
if turtle.is_looking_at_boundary:
turtle.select()
break
new_face_mask = turtle.get_selection_mask()
bm.free()
out_face_mask.append(new_face_mask)
print("Done")
This example API can work with faces only; but we can make similar API for edges and vertices selection.