2
$\begingroup$

I want to convert selected vertices/edges into curves. I can convert them using bpy.ops functions. But is there any alternative using lower level api preferable using bmesh. I can get all spline points to proper position but not in proper order so I get this mesh(middle) not the expected curve(left). enter image description here

Here is the code.

import bpy
import bmesh

def create_curve(coords_list):
    # make a new curve
    crv = bpy.data.curves.new('crv', 'CURVE')
    crv.dimensions = '3D'

    # make a new spline in that curve
    spline = crv.splines.new(type='POLY')

    # a spline point for each point
    spline.points.add(len(coords_list)-1) # theres already one point by default
    
    # assign the point coordinates to the spline points
    for p, new_co in zip(spline.points, coords_list):
        x,y,z = new_co
        p.co = (x,y,z,1)
    
    # make a new object with the curve
    obj = bpy.data.objects.new('object_name', crv)
    bpy.context.scene.collection.objects.link(obj)
    
def main(context):
    
    obj = context.active_object
    if obj.mode == 'EDIT':
        # this works only in edit mode,
        bm = bmesh.from_edit_mesh(obj.data)
        selected_verts = list(filter(lambda v: v.select, bm.verts))
        pos = list(map(lambda v: v.co,selected_verts))
        
        create_curve(pos)

class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"
    bl_label = "Simple Object Operator"

    def execute(self, context):
        main(context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(SimpleOperator)


def unregister():
    bpy.utils.unregister_class(SimpleOperator)


if __name__ == "__main__":
    register()

There is also this stack overflow thread. Solution in that thread works for closed loop. I tried to modify it to work for non-closed loop/selected edges but no success. Modified script works in some cases in other gives weird results.

$\endgroup$

1 Answer 1

6
$\begingroup$

Walk the edges.

To give them some order walk the edges. It may take more than one spline to make the curve, since we cannot branch. For example the default cube when "de-faced" and converted to curve has 4 splines.

Here is test script to walk the selected edges. It does not make the curve, rather creates an ordered list of the edges to use in each spline.

  • Get all selected edges.
  • Pop an edge, get its two end verts. Tag the edge.
  • Grow spline from each end on a non tagged and selected edge, prepend at one end, append at other.
  • Add spline to splines.
  • Repeat until no more edges to process.

Script

import bpy
import bmesh
from collections import defaultdict

from bpy import context
tag = defaultdict(bool)

ob = context.edit_object
me = ob.data
bm = bmesh.from_edit_mesh(me)
# selected edges
edges = list(filter(lambda e: e.select, bm.edges))
# all edges
#edges = bm.edges[:]

splines = []
def tag_remove(edge):
    tag[edge] = True
    edges.remove(edge)

def grow(end, spline, idx):
     while True:
        nextedge = next(
            (e for e in end.link_edges if not tag[e] and  e.select), 
            None
            )
        if not nextedge:
            return
        tag_remove(nextedge)
        spline.insert(idx, nextedge.index)
        end = nextedge.other_vert(end)   
    
while edges:

    edge = edges.pop()
    spline = [edge.index]
    tag[edge] = True
    a, b = edge.verts
    grow(a, spline, 0)
    grow(b, spline, len(edges))
    splines.append(spline)
    
print(splines)

Result for default cube. Notice 4 splines as with convert.

[[5, 1, 0, 3, 6, 11, 7], [4, 10], [2, 9], [8]]

Result for default circle. Since edge 30 shares a vert with edge 31 could make this cyclic.

[[30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 31]]

To reverse order, could try swapping which end to append / prepend...

$\endgroup$

You must log in to answer this question.

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