# Rotate vertices to any arbitrary rotation

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.

Code:

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

def update_bm_verts(bm):
bm.verts.index_update()
bm.verts.ensure_lookup_table()

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
obj.select_set(True)
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:
bm.verts.new(v)
update_bm_verts(bm)

bm.edges.new(bm.verts)

bm.to_mesh(mesh)
bm.free()

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

for v in verts:
bm.verts.new(v)
update_bm_verts(bm)

bm.faces.new(bm.verts)

bm.to_mesh(mesh)
bm.free()

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]

##########################################
# PLOT AND ROTATION
def rotate_arc_points_towards(
):

norm_rotation = verts_norm.rotation_difference(target_norm).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_norm = Vector((0, 0, 1))

# target rotation

d = (ed.copy() - st.copy()).normalized()

#    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)


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
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),
)
)
mtrx.normalize()

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