Make bone roll match a face/vertex normal? I am working on a script that generates bones down an edge or face loop. I have figured out how to create a bone and move its head and tail into position at the top and bottom of a face, or onto a specific vertex. The problem now is how to roll the bone so that it is flat on the face normal (or if on an edge loop, so that it based on the vertex normals.)

Is there some existing way to do this, or any existing addons/scripts that handle this problem? Or do I have to somehow derive the bone’s angle?
Construct the bone matrix

Here is a script that creates a  bone at each edge such that the bones y axis is aligned vert 0 to vert 1 and the z axis to the &quot;edge normal&quot; ie the average of the two linked face normals.
Select mesh object in object mode, run script creates an armature with bone on each edge.
import bpy
import bmesh
from mathutils import Vector, Matrix
context = bpy.context
vl = context.view_layer
coll = context.collection
ob = context.object # the mesh
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
arm = bpy.data.armatures.new(&quot;Foo&quot;)
arm_obj = bpy.data.objects.new(&quot;Foo&quot;, arm)
coll.objects.link(arm_obj)
vl.objects.active = arm_obj
bpy.ops.object.mode_set(mode='EDIT')

for e in bm.edges:
    eb = arm.edit_bones.new(f&quot;edge{e.index}&quot;)
    v = e.verts[1].co - e.verts[0].co
    
    y = v.normalized()
    z = sum((f.normal for f in e.link_faces), Vector()).normalized()
    x = y.cross(z)
    M = Matrix((x, y, z)).transposed().to_4x4()
    M.translation = e.verts[0].co
    eb.matrix = M
    eb.tail = e.verts[1].co

bpy.ops.object.mode_set()