2
$\begingroup$

I have a list of vertices.
I want to move the verts/white squares along their own paths like so: enter image description here

(Assume the polygon is closed)
How can I do that with python and get a list of shifted vertices back?

$\endgroup$
5
  • 1
    $\begingroup$ Every time I read this I get more confused. Is setup something akin to this answer blender.stackexchange.com/a/133032/15543 sliding edges (connected vertices?) along a path. Is the goal not to have the icospheres intersect? $\endgroup$
    – batFINGER
    Sep 6, 2021 at 14:41
  • 1
    $\begingroup$ Perhaps OP needs a tool like vertex slide, except in that tool at start a copy of geometry would be made, then converted to a path, then all vertices would be moved towards either of two ends of the path, depending in which way the mouse is moving... $\endgroup$ Sep 6, 2021 at 14:44
  • 1
    $\begingroup$ I completely rewrote the question, hope it's clear now. @Markus von Broady You are right, vertex slide, but verts can travel beyond their adjacent neighbours. $\endgroup$ Sep 6, 2021 at 15:13
  • $\begingroup$ Ok think I have it. As mentioned, convert a copy to a path and then flatten the edges and feed onto it as in first link commented. See blender.stackexchange.com/questions/214036/… $\endgroup$
    – batFINGER
    Sep 6, 2021 at 16:51
  • $\begingroup$ Not sure I understand. Flatten the polygon and doing the shifting on a straight line and then transform back? How does transforming back look like in python? $\endgroup$ Sep 7, 2021 at 7:52

1 Answer 1

0
$\begingroup$

Here is the solution I ended up using. Suggestions welcome.

import bpy
import numpy as np

#assumes closed polygon with vertices in linear order 
#creates new objetc with shifted/slid verts
#https://blender.stackexchange.com/questions/191649/how-can-i-sort-vertex-positions-sequentially-indices-in-a-closed-area
def multi_slide_verts(obj, shift_length):

    def read_verts(obj):
        length = len(obj.data.vertices)
        coords = np.empty(length*3, dtype=np.float64)
        obj.data.vertices.foreach_get('co',coords)
        coords.shape = (length, 3)  
        return coords #numpy array of vert array [[x,y,z],[x,y,z]...]

    def write_obj_from_pydata(name, verts, edges=None, close=True):
        if edges is None:
            # join vertices into one uninterrupted chain of edges.
            edges = [[i, i+1] for i in range(len(verts)-1)]
            if close:
                edges.append([len(verts)-1, 0]) #connect last to first
                
        me = bpy.data.meshes.new(name)
        me.from_pydata(verts, edges, [])   
          
        obj = bpy.data.objects.new(name, me)
        bpy.context.scene.collection.objects.link(obj)


#collect segments, their length and directions in parallel numpy arrays
    P1 = read_verts(obj) #all verts
    P2 = np.roll(P1, -1, axis=0)
    segs = P2-P1
    dists = np.linalg.norm(P2-P1, axis=1)+0.000000001  
    dirs = segs/dists[:, np.newaxis]  #.np.new axis to give same shape, https://stackoverflow.com/questions/7140738/numpy-divide-along-axis


    shifted_pts = [] 
    for i in range(len(P1)):#iterate over all points
                    
        #normal/easiest case, shift point on own segment in segment direction
        if dists[i]>=shift_length:#dist is big enough
            copy_original_pt = P1[i][:]
            shifted_pt = copy_original_pt+(dirs[i]*shift_length)
            shifted_pts.append(shifted_pt)
                
      
        #case where you need to look beyond neighbouring verts for shifting direction/dist
        else: #dist is not enough to apply shift, so you need to lookahead and see if next seg dist is enough.
            traveled_dist = dists[i]
            lookahead = 0 #number of segments to skip to have enough for shift length
        
            while traveled_dist < shift:  
                                         #check if next segment is enough and note down traveled_dist
                lookahead+=1
                    
                #to make lookahead indices stay in range when looking further than last point
                rotate_index = (i+lookahead)%len(dists) #rotate i lookahead goes further then last point, cause for now your path is cyclic!     
                traveled_dist+=dists[rotate_index] 
                
            #same for distance to calculate rest amount you didn't travel yet.
            shift_rest = shift - (traveled_dist-dists[rotate_index])
            copy_original_pt = P1[rotate_index][:]
            shifted_pt = copy_original_pt+(dirs[rotate_index]*shift_rest)
            shifted_pts.append(shifted_pt)
                        
    write_obj_from_pydata('shifted_pts', shifted_pts, edges=None, close=True)


obj = bpy.context.active_object
multi_slide_verts(obj, 9.3)
$\endgroup$

You must log in to answer this question.

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