align vertices on general axis by script?

Hello everyone,
I’m looking for some ideas on how align/project some vertices belonging to a group on general axis passing through two specific vertices A and B (just to give them a name)

I was thinking to place the cursor on A, rotate the local coord system in order to get X (or Y or Z) axis pointing to B and then scale the vertex of the group.
But I0m not quite sure it would work plus get the correct rotation of the system is not so simple…

Any idea?

thanks
Giuseppe

So there’s a virtual line between A and B, and you want to place other vertices (where do they come from?) on that virtual line?

Isn’t that what the Shrinkwrap Modifier does if limited to a Vertex Group?

yes, almost. I mean, let’s consider a shirt XXXS size to be adapted on a guy XXXL: shrinwrap modifier will project the shirt vertices on the guy, but you will get just a little tattoo in the middle of the chest of the big guy :slight_smile:
You will need first to strecth the shirt boundaries and after that shrinkwrap modifier can do the job.
To do that I thought an algorithm as written in my previous post

Not sure i follow since your last post, but for aligning verts to an arbitrary line you could construct a space aligned to that line and bring the verts to that space.

x = (B - A).normalized()
y = Vector([0,0,1]).cross(x).normalized()
z = x.cross(y).normalized()

line_space = Matrix(x,y,z)

You can use this space to translate verts on an axis aligned with your input line. Here your line would be the x axis of the matrix, so to align you simply null yz or give them all the same yz value.

Heres a very rough example of what i mean.


import bpy
import bmesh
from math import *
from mathutils import *

class AlignOnAxis :
    
    C = bpy.context
    AO = None
    BM = None
    VSpace = None
    AlignPoints = []
    EndPoints = []
    
    
    def __init__(self):
        
        self.AO = self.C.active_object
        self.BM = bmesh.from_edit_mesh(self.AO.data)
        
        self.getSelection()
        self.VSpace = self.constructSpace()
        self.PointsToAxis()
        
        bmesh.update_edit_mesh(self.AO.data,False,False)
         
    
    def getSelection(self):
        
        for i in range(len(self.BM.select_history)):
            if i==0 or i==len(self.BM.select_history)-1 :
                self.EndPoints.append(self.BM.select_history[i].index)
            else :
                self.AlignPoints.append(self.BM.select_history[i].index)
        
    
    def constructSpace(self):
        
        bmv = self.BM.verts
        mW = self.AO.matrix_world
        
        v1 = mW * bmv[self.EndPoints[0]].co
        v2 = mW * bmv[self.EndPoints[1]].co
        
        Vx = (v2 - v1).normalized()
        Vy = Vector([0,0,1]).cross(Vx).normalized()
        Vz = Vx.cross(Vy).normalized()
        
        VertSpace = Matrix( [Vx , Vy , Vz] )
        
        return VertSpace
    
    
    def PointsToAxis(self):
        
        bmv = self.BM.verts
        mW = self.AO.matrix_world
        
        originPos = mW * bmv[ self.EndPoints[0] ].co
        originPos = self.VSpace * originPos
        
        for v in range(len(self.AlignPoints)) :
            
            vert = bmv[self.AlignPoints[v]]
            vpos = mW * vert.co #to world
            vpos = self.VSpace * vpos #to VSpace
            
            vpos.y = originPos.y
            vpos.z = originPos.z
            
            vpos = self.VSpace.inverted() * vpos
            vpos = mW.inverted() * vpos
            vert.co = vpos



#====---RUN---====#
AlignOnAxis()

To use, go in edit mode on a mesh and select some verts (>2).
The first and last selected verts are the A and B points. The other verts will be snapped to this line.
After selecting, just run the script.