I'm trying to rotate a set of vertices (in the example code they form a triangle but will be an arc) to any arbitrary rotation. I start by building a base triangle on the X,Y plane with a Z normal. The top of the triangle is heading in the Y direction. Then using a target heading for the triangle top direction, I calculate a new normal and pass all into a rotation function, that should rotate the base triangle verts to the target rotation.

I know that my target normal and target heading are being calculated correctly but my code only works in some directions and not others. For instance, at the bottom of the code below, I set my target triangle parameters. Changing the Y direction (m in the code) to 1 instead of -1 produces a -Z normal and the rotation is completely wrong as it rotates to an X normal.

I've hardcoded the pivot point to (0,0,0) for simplicity. The important code is at the bottom bellow the #### and I've left in various commented out triangle headings for testing.

I know there must be something wrong with my matrix transformations but I can't see it. Thanks for any advice.


import bpy
import bmesh
import math
from mathutils import Vector, Quaternion, Matrix

def update_bm_verts(bm):

def setup_new_obj(name, context):
    obj_name = name
    mesh = bpy.data.meshes.new("mesh")
    obj = bpy.data.objects.new(obj_name, mesh)
    context.view_layer.objects.active = obj
    mesh = context.object.data
    bm = bmesh.new()
    return obj, mesh, bm

def build_normal(verts, context):
    obj, mesh, bm = setup_new_obj("NEW_NORM", context)
    vs = None
    for v in verts:


def build_face(verts, context):
    obj, mesh, bm = setup_new_obj("NEW_ARC", context)

    for v in verts:


def find_edge_center(pt1, pt2):
    mx = (pt1.x + pt2.x) / 2
    my = (pt1.y + pt2.y) / 2
    mz = (pt1.z + pt2.z) / 2
    v = Vector((mx, my, mz))
    return v

def translate_pos(v1, mtrx):
    for v in mtrx:
        v[0] += v1[0]
        v[1] += v1[1]
        v[2] += v1[2]

def rotate_arc_points_towards(
    verts, verts_heading, target_heading, verts_norm, target_norm, pivot

    norm_rotation = verts_norm.rotation_difference(target_norm).to_matrix().to_4x4()
    mat = verts_heading.rotation_difference(target_heading).to_matrix().to_4x4()

    pivot = Vector((0, 0, 0))
    M = Matrix.Translation(pivot) @ norm_rotation @ mat @ Matrix.Translation(-pivot)    

    return [M @  v for v in verts]

def plot_tri(pts):
    # tri start, end, peak and edge center
    st = pts[0].copy()
    ed = pts[1].copy()
    md = pts[2].copy()
    cm = find_edge_center(st, ed)
    # base triangle
    tri_verts = [Vector((-1,0,0)), Vector((1,0,0)), Vector((0,1,0))]
    tri_heading = Vector((0, 1, 0))
    tri_norm = Vector((0, 0, 1))
    # target rotation
    target_heading = cm.copy() - md.copy()
    d = (ed.copy() - st.copy()).normalized()
    target_norm = d.cross(target_heading)

    print("T Heading = {0}, A Heading = {1}, D = {2}, T Normal = {3}".format(target_heading, tri_heading, d, target_norm))
    if Vector(target_heading).magnitude > 0:
        verts = rotate_arc_points_towards(tri_verts, tri_heading, target_heading, tri_norm, target_norm, cm)

#    translate_pos(st, verts)
    return verts

## X Peak
#s = Vector((0, -1, 0))
#e = Vector((0, 1, 0))
#m = Vector((1, 0, 0))

## Y Peak - works with -y not y
s = Vector((-1, 0, 0))
e = Vector((1, 0, 0))
m = Vector((0, -1, 0))

# Z Peak - works both z and -z
#s = Vector((0, 0, 0))
#e = Vector((3, 0, 0))
#m = Vector((1.5, 0, 1))

tri_verts =  plot_tri([s, e, m])
build_face(tri_verts, bpy.context)

1 Answer 1


If anyone is interested in the code, I've just solved the problem. It was a simple case of creating a new coordinates system with the target rotation, then converting the base triangle verts to the new system. I'm sure there's a more elegant way to do it but this seems to work fine. Code is below for the new rotation function.

def rotate_arc_points_towards(verts, target_heading, target_norm, d):
    x = d
    y = target_heading
    z = target_norm

    # The matrix for the target coordinate system
    mtrx = Matrix(
            (x[0], y[0], z[0], 0),
            (x[1], y[1], z[1], 0),
            (x[2], y[2], z[2], 0),
            (0, 0, 0, 1),

    # move verts into new coordinates system
    return [mtrx @ v for v in verts]  

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .