2

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?

Tortenrandband
  • 771
  • 6
  • 13
  • 1
    Every time I read this I get more confused. Is setup something akin to this answer https://blender.stackexchange.com/a/133032/15543 sliding edges (connected vertices?) along a path. Is the goal not to have the icospheres intersect? – batFINGER Sep 06 '21 at 14:41
  • 1
    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... – Markus von Broady Sep 06 '21 at 14:44
  • 1
    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. – Tortenrandband Sep 06 '21 at 15:13
  • 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 https://blender.stackexchange.com/questions/214036/how-to-flatten-a-vertex-path-a-list-of-connected-edges-into-a-straight-line-wh – batFINGER Sep 06 '21 at 16:51
  • 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? – Tortenrandband Sep 07 '21 at 07:52

1 Answers1

0

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)

Tortenrandband
  • 771
  • 6
  • 13