In order to select vertices with a certain number of steps we may perform the following steps:
Mesh
.This approach is straightforward and simple. The function that is responsible for the actual work is shown below (the full script is called poleselect1.py
). It follows our outline closely. The actual selection of a vertex is effected by assigning to the sel
attribute of the vertex. Note also that the v1
and v2
attributes of an edge
object are not indices into the verts
attribute of our mesh but refer to MVert
objects. That is why we need to retrieve the index
attributes to compare.
def poleselect1(me,n=5):
for v in me.verts:
n_edges=0
for e in me.edges:
if e.v1.index == v.index or e.v2.index == v.index:
n_edges+=1
if n_edges >= n:
v.sel = 1
break
You probably noticed that we iterated over the list of edges for each and every vertex (highlighted in the previous code). This might be costly in terms of performance and this cost is even compounded by the need to compare indices which have to be retrieved again and again. Is it possible to write more efficient code that stays readable nonetheless? It is if we follow this strategy:
Mesh
.By using this strategy we perform just two possibly lengthy iterations at the cost of needing the memory to store the dictionary (nothing is free). The speed increase is negligible for small meshes but might be considerable (I clocked a 1,000-fold speed boost on a smallish mesh of 3,000 vertices) for large meshes, and those are just the kind of meshes where someone might need a tool like this.
Our revised selection function is shown below (the full script is called poleselect.py
). First note the import
statement. The dictionary that we will be using is called a default dictionary and is provided by Python's collections module. A default dictionary is a dictionary that initializes missing items the first time they are referred to. As we want to increment the count for every vertex that is referred to by an edge, we should either initialize our dictionary with a zero value for every vertex in the mesh beforehand or check if a vertex is already indexed every time we want to increment the count and, if not, initialize it. A default dictionary does away with the need to initialize everything in advance and allows for a very readable idiom.
We create our dictionary by calling the defaultdictionary()
function (a function returning a new object whose behavior is configured by some argument to the function is called a factory in object-oriented circles) with an int
argument. The argument should be a function taking no arguments. The built-in function int()
that we use here will return an integer value of zero when called without arguments. Every time we access our dictionary with a non-existing key, a new item is created and its value will be the return value of our int()
function, that is, zero. The essential lines are the two where we increment the edgecount
(highlighted part of the following code). We could have written that expression in a slightly different way to illustrate why we need a default dictionary:
edgecount[edge.v1.index] = edgecount[edge.v1.index] + 1
The dictionary item we refer to on the right-hand side of the expression might not yet exist every time we refer to a vertex index that we encounter for the first time. Of course, we could check beforehand but that would render the code a whole lot less readable.
from collections import defaultdict
def poleselect(me,n=5):
n_edges = defaultdict(int)
for e in me.edges:
n_edges[e.v1.index]+=1
n_edges[e.v2.index]+=1
for v in (v for v,c in n_edges.items() if c>=n ):
me.verts[v].sel=1
18.116.21.152