I have this simple setup:
I'd like to walk this perimeter and find the total length of every side, so as to find which side is longer:
How can I do that with a python script? Thank you EDIT: added a clearer picture
Co linear Edges
below is a script that returns all co linear edges from a set of input edges. Two edges are colinear if they share a vertex and the edge vectors are parallel (dot product of normalized vectors is one)
The method edge_line_segments(...)
takes as its arguments, the bmesh, a list of edges from that bmesh and a tolerance angle for how far from parallel the edges can be.
The return dictionary has a key "segments" which is a list of edges in each line segment.
import bpy
import bmesh
from math import sin, radians
def tag(iterable, value):
for x in iterable:
x.tag = value
def vec(e):
v0, v1 = e.verts
return (v1.co - v0.co).normalized()
def parallel(e1, e2, tol_angle=0):
return abs(vec(e1).dot(vec(e2)) - 1) <= sin(tol_angle)
def line_select_extend(edge, v, tol_angle=0):
edges = []
while v:
segments = [e for e in v.link_edges
if not e is edge
and e.tag
and parallel(e, edge, tol_angle)]
if segments:
edge = segments[0]
edges.extend(segments)
v = edge.other_vert(v)
else:
v = None
return edges
def select_segment(edge, tol_angle=0):
v0, v1 = edge.verts
edges = line_select_extend(edge, v0, tol_angle)
edges.reverse()
edges.append(edge)
edges.extend(line_select_extend(edge, v1, tol_angle))
return edges
def edge_line_segments(bm, edges=[], tol_angle=0):
ret = {"segments" : []}
tag(bm.edges, False)
tag(edges, True)
edges = set(edges)
while edges:
segments = select_segment(next(iter(edges)))
ret["segments"].append(segments)
edges -= set(segments)
tag(bm.edges, False)
return ret
To use.
Test script below to run in edit mode. Calls method with boundary (perimeter edges), sorts the segments by length, selects the edges of the longest segment and writes a report to system console.
context = bpy.context
ob = context.object
me = ob.data
bm = bmesh.from_edit_mesh(me)
perimeter = [e for e in bm.edges if e.is_boundary]
ret = edge_line_segments(bm, edges=perimeter, tol_angle=radians(5))
segments = [(sum(e.calc_length() for e in s), s)
for s in ret["segments"]]
segments.sort(key=lambda s: s[0])
for i, (length, edges) in enumerate(segments):
print("linked segment %d edges length %.3f" % (len(edges), length))
for e in edges:
e.select = i == len(segments) - 1
print("\t edge %d len %.2f" % (e.index, e.calc_length()))
With the following script you're able to find the indexnumber of the corner vertices. Just put the object in editmode and select all corner vertices. The script will print a list for the second script.
import bpy
import bmesh
obj = bpy.context.active_object
mesh = obj.data
bm = bmesh.from_edit_mesh(mesh)
bm.faces.ensure_lookup_table()
vertices = bm.faces[0].verts
corner_verts =[]
for v in vertices:
if v.select:
corner_verts.append(v.index)
print(corner_verts)
bm.free()
bpy.ops.object.mode_set(mode='OBJECT')
Replace the corner_verts array with the output from the first script.
import bpy
import bmesh
obj = bpy.context.active_object
mesh = obj.data
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(mesh)
bm.verts.ensure_lookup_table()
corner_verts = [3, 11, 10, 1, 6, 5, 0, 15, 14, 2, 13, 12]
len_corner_verts = len(corner_verts)
for i in range(0, len_corner_verts):
v1 = bm.verts[corner_verts[i % len_corner_verts]].co
v2 = bm.verts[corner_verts[(i + 1) % len_corner_verts]].co
dv = v2 -v1
print('from {0} to {1}, length: {2}'.format(v1, v2, dv.length))
print('---')
bm.free()
bpy.ops.object.mode_set(mode='OBJECT')