How to add vertices with even spacing on a line mesh in python?

Hi,
Can anyone help to advise how I can add vertices with even spacing to a line mesh? Better still first remove all existing vertices (except the 2 ends) then add even spacing … and if possible specific space in between vertices.

My final objective is to work on a maze with many lines (mesh).
The manual approach of using “subdivide” is not working well and very tedious everytime I have a new maze pattern.

Here’s an existing thread I seek help but I think if anyone with good “python” knowledge in Blender environment maybe could assist to solve this problem.

Pretty sure you will need Bmesh for this.

BMesh Module (bmesh) — Blender Python API

Haven’t used it myself so i can’t provide the python answer but you might want to consider converting to mesh and using/playing with limited dissolve.(edit mode> mesh> delete> limited dissolve). That could get rid of unnecessary vertices. then its just a matter of subdividing each edge and applying a bevel to the resulting center vertices based on the edge length and amount of segments.

If i were you, i’d make an operator that can handle 1-x amount of edges, make it a modal operator where you can see and act on the result ‘live’ using the mousewheel to increase segments.

in terms of python,

How to subdivide mesh with Python and Blender 2.8? - Blender Stack Exchange

or to dissolve single vertices:

bmesh.utils. vert_dissolve (vert )

If you can’t work with limited dissolve for some reason, doing it manually requires checking if a vertice lays at an X or T intersection. Not sure how that is done in practice. The angle could be used. But this is exactly what limited dissolve can do. So there’s no need to re-invent the wheel.

applying bevel to vertex/edge/face

bmesh.ops. bevel

Once you subdivide an edge, you have 2 edges. center vertice is the vertice that they both share. So that’s relatively easy to figure.The rest is just math really. If combined length is 1m and segments are 25cm, you’d apply a bevel of 2 segments at a width( or length in this case) of 50cm.

If you use lineart modifiers you will not need curves of any kind btw!

ok thanks. I basically use this “limited disolve” vertices and re do segments with subdivide manually to solve my problem. I don’t really understand the rest of your answers like bmesh … what’s the difference between mesh and bmesh ?

As for “lineart” modifiers … this is something I tried. Seems like it trace outline of an objects or something. Don’t really see how this can solve this “even spacing vertices” I needed to create on an existing curve line.

As for “lineart” modifiers … this is something I tried. Seems like it trace outline of an objects or something. Don’t really see how this can solve this “even spacing vertices” I needed to create on an existing curve line.

It wasn’t sure to me if your case was identical to the post you linked. Since it mentions curves. My point merely being that with lineart modifiers you can use actual geometry. Which can be easier to manipulate if you only need 90 degree intersections/corners. Curves are more work/requiring proper handle and interpolation settings. In practice it matters little because you can convert edges to curves and back if you still prefer the options grease pencil and curves offer.

About Bmesh:

Bmesh is just a name to group code that can deal with meshes.

You can use bmesh to see which edges you have selected. Allowing you to make an operator that works on any number of edges that you may have selected and handle them at once. There are also mesh operators. But for these to work properly in your case, and in the event of having multiple edges selected, you’d have to keep (de)selecting the edge you want to work on. To store a reference to each edge in that case, you’d still need bmesh.

import bpy, bmesh

myobject = bpy.context.object.data

bm = bmesh.new()
bm.from_mesh(myobject)
for edge in bm.edges:
    if edge.select = True
        #Do something with selected edge

to apply your changes to your object:

bm.to_mesh(myobj.data)
myobj.data.update()

So, combining these things, You’d want to make a list of every edge you want to do something with. Then maybe store all old vertices in a variable(or like i mentioned before, see which vertice is shared by the two new edges). Subdivide an edge, check which vertice is the new vertice and then bevel it(update your old vertices variable at this point). Then move on to the next edge.

Might also have to use this:

bm.free()

I recommend searching recent examples for each of the steps needed. I’m not 100% sure this is syntactically correct. You definitely don’t have to do it manually for each edge. So the most flexible would be an operator that allows you to adjust N amount of edges. Then you still have to look at and select each edge at some point, just a lot faster. Doing it somewhat randomly is also an option but i imagine that would not give the results you’d be looking for. Not without some fiddling at least.

ok. Now I understand bmesh is really what I should be looking deeper in.
Will it be too much trouble if you can provide me a complete code set that allow me to loop through all selected edges, and adjust the “length” on each of them to be the same?

That way I can manually subdivide as needed and select all edges (similar distance) and then apply this “bmesh code” (hopefully you can provide) to make all edges equal length, and manually adjust any edges length (as there will be some at corners that may be slightly shorter).

Thanks in advance.

I haven’t used bmesh myself yet and testing wether it works for your case is something only you can do. It would take more time than i have right now to provide you with a working example.

import bpy, bmesh

myobject = bpy.context.object.data
yourinput = bpy.context.scene.yourpropertygroup.yourinputfield
bm = bmesh.new()
bm.from_mesh(myobject)

edgelist []
vertlist []

bsegments = yourinput-2 # a number that reflects the amount of segments you want
    
for edge in bm.edges:
    if edge.select = True
        edgelength = edge.calc_length()
     
        bamount = edgelength/yourinput*(yourinput-2)

        edgelist = edgelist.append(edge)
        bmesh.ops.subdivide_edges(bm,
                          edges=edgelist,
                          cuts=1,
                          use_grid_fill=True,
                          )
       edgelist.clear()

         

        vertlist = vertlist.append(bm.verts[-1])
                bmesh.ops.bevel(bm,
                geom=vertlist,
                offset = bamount,
                offset_type='OFFSET',
                segments=bsegments,
                profile=0.5,
                affect = 'VERTICES',
                clamp_overlap=True,
                material=-1)
        vertlist.clear()





You’d want to create an input field and an button to invoke the operator in blender. or bind it to a hotkey. Then create an operator that reads the value of that input field(should be an int value). Then run the for loop. It doesn’t do what you asked for precisely. Don’t enter a number lower than 3. Also note. for even numbers, simply subdividing should suffice. So you could contain the code to be only ran on uneven numbers equal to or larger than 3. On even numbers you can subdivide N-(n/2) times.

also note, consider this pseudo code. Some syntax might be incorrect and the indentation is horrible. I wrote it in the browser. There are examples in blender under the scripting tab in the topbar. That show you how to make an operator. You can add everything needed in that script and when it works like you need to, save it and run as needed/register it as add-on etc.

You could move this line:

bsegments = yourinput-2 # a number that reflects the amount of segments you want

into the for loop and change it to something that bases the amount of segments on the edge length/yourinput. round this down. then yourinput would have to provide a different value, a float. reoresenting the distance between two vertices. Won’t be perfect. If you can ensure maze path lengths of even numbers and only ask for segments that are an even number long, then it might work out.

Thanks Zender,
Just so you know I don’t think I am there yet … in figuring out how to orchestrate this codes and tweet it to work for my needs. So I will ponder this one and come back when I am more ready to “read codes” :slight_smile:
I really appreciate you spend time on this “pseudo code” to help solve the problem I am facing. I will do it manually for now to finish off this mini project of mine.

1 Like

Ohkay so I read through your thread more carefully. have you tried extruding your lines on the global Z axis and then applying limited dissolve, i noticed the screenshots where there are still some bushes ending up on place where edges without intersections still have a center vertice. limited dissolve should be able to get the few that were left as well.

If you used maze generator, you can do what you want to, by simply subdividing an edge based on edge length / your preferred length…

So you’d take a line, divide its length by cell length or width ( assuming square cells here) and then add the amount of segments you’d want on a per cell basis. You could measure the cell in blender or expose it through a small script in blender. you’d convert the curves to mesh, extrude, limited dissolve, remove unwanted goemetry and then select the edge you want to measure. but just zooming in with the ruler is accurate enough most likely.

This gets the length of a selected edge: you might have to re-enter edit mode for the bmesh to get the mesh properly i noticed.

import bpy, bmesh

myobject = bpy.context.object.data
bm = bmesh.new()
bm.from_mesh(myobject)
edgelist =

for edge in bm.edges:
if edge.select == True:
edgelength = edge.calc_length()
print(edgelength)
image

image

I hope this helps, you can work on that script until it does what you want, i just copy-pasted it into the console in my scripting worktab.

perhaps this is of help too:

import bpy

class SimpleOperator(bpy.types.Operator):

bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"


def execute(self, context):
    import bmesh
    myobject = bpy.context.object.data
    bm = bmesh.new()
    bm.from_mesh(myobject)
    edgelist = []

    for edge in bm.edges:
        if edge.select == True:
            edgelength = edge.calc_length()
            print(edgelength)
    
    return {'FINISHED'}

addon_keymaps =

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

wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
    km = wm.keyconfigs.addon.keymaps.new(name='3D View', space_type='VIEW_3D')
    kmi = km.keymap_items.new('object.simple_operator', type='W', value='PRESS', ctrl=True)
    addon_keymaps.append((km, kmi))

def unregister():
bpy.utils.unregister_class(SimpleOperator)
# Remove the hotkey
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()

if name == “main”:
register()

bpy.ops.object.simple_operator()

This adds that code in an operator and binds the operator to a shortcut: ‘CTRL + W’

Can see its output if you open system console:
image

Thanks Zender,
Just so you know I don’t think I am there yet … in figuring out how to orchestrate this codes and tweet it to work for my needs. So I will ponder this one and come back when I am more ready to “read codes” :slight_smile:
I really appreciate you spend time on this “pseudo code” to help solve the problem I am facing. I will do it manually for now to finish off this mini project of mine.

I will take your advise on using the maze generator instead … good to know I can setup length/segments on edges and adjust using “dissolve vertice” approach.

Thanks again !

1 Like

Good luck!

Say Alexander,
What about a python scripts that allow me to add vertices to an edge say on every 1 metre length? that way I will save lots of work manually go to each edges (>90 edges per maze) to subdivide and dissolve and redo where the spacing is different.

Assuming all unnecessary geometry is gone already, you could write something along these lines:

import bmesh
import math
myobject = bpy.context.object.data
bm = bmesh.new()
bm.from_mesh(myobject)


for edge in bm.edges:
    if edge.select == True:
        edgelength = edge.calc_length() 
        spacing = 1.0                                   # the desired distance between 2 vertices.
        segments = edgelength / spacing                 # output would be 4.0
        subdXtimes = segments - (segments / 2 )         # output would be 2.0
        subdXtimes = subdXtimes.math.Floor()            # Rounding output down to nearest integer, 2

        bmesh.ops.subdivide_edges(bm,
                      edges=edge,
                      cuts=subdXtimes,
                      )

You’ll still need to apply the bmesh to your actual mesh though to actually get the results. Don’t do that in the for loop but after it’s done. I can imagine that this runs an error because bm.edges will change as you loop over it and subdivide it. If this happens, you should make another variable like edges = bm.edges and loop over edges instead of bm.edges but apply the subd on the same edge in bm.edge. This can be done by using the keys. so edges[0] would be the same edge as bm.edges[0]. Not sure if this will be an error but i think it will. To implement that you’d simply add a count variable starting at zero and update this count after the edge got subdivided:

before for loop

count = 0

in for loop(not exactly like this but to indicate what you should use then)

edges[count]
bm.edges[count]
count += 1

Thanks a lot Zender.
This seems to work … I will try to play around it with new mazes.
You are truly good and your speedy support is much appreciated.

1 Like

Hi Zender,
I have completed my 1st maze manually with subdivide, count and redo for each edges of my maze. Very tedious …

I am now working on my 2nd maze and wanted to try your suggested code to automate. I am really really raw on Python and bmesh. I tried to run your code using a simple cube to see if any errors. Well it does … stuck in line 7. See the complete code setup below. Could you help to complete it so that it works even on a simple cube and subdivide it with even spaces and adding vertices to it?

import bpy, bmesh
import math
myobject = bpy.context.object.data

Create a new BMesh instance.

bm = bmesh.new()
bm.from_mesh(myobject)

edges = bm.edges
for edge in bm.edges:
if edge.select == True:
edgelength = edge.calc_length()
spacing = 1.0 # the desired distance between 2 vertices.
segments = edgelength / spacing # output would be 4.0
subdXtimes = segments - (segments / 2 ) # output would be 2.0
subdXtimes = subdXtimes.math.Floor() # Rounding output down to nearest integer, 2

    bmesh.ops.subdivide_edges(bm,
                  edges=edge,
                  cuts=subdXtimes,
                  )

bpy.ops.object.mode_set(mode=‘OBJECT’)

Convert BMesh to mesh data, then release BMesh.

bm.to_mesh(myobject)
bm.free()

I think math.Floor() should be math.floor() the console also complains about indentation for the next lines. (python uses indentation/whitespaces as a way to organize code).

bm = bmesh.new()

bm.from_mesh(myobject)

edges = bm.edges

for edge in bm.edges:

if edge.select == True:

    edgelength = edge.calc_length()

    spacing = 1.0 # the desired distance between 2 vertices.

    segments = edgelength / spacing # output would be 4.0

    subdXtimes = segments - (segments / 2 ) # output would be 2.0

    subdXtimes = subdXtimes.math.floor() # Rounding output down to nearest integer, 2

    bmesh.ops.subdivide_edges(bm,

    edges=edge,

    cuts=subdXtimes,

    )

bpy.ops.object.mode_set(mode=‘OBJECT’)

bm.to_mesh(myobject)

bm.free()

i recommend using visual studio or another IDE to actually write the code in

Hmm guess I need to give up this approach. Just not a coder lol.
Keep getting error …

The first few scripts are the hardest. if you do want to use an IDE though, there is an add-on for visual studio that helps developing add-ons. You can use ctrl shift + P to launch blender with your add-on with that. Works like a charm. I also recommend reading a bit into python it self. You’re really learning at least 2 things here, python and the blender API. Being able to read and recognize which is what is pretty important. Just let me know if you don’t understand an error.

I also recommend using the example in post 9. Where I put the code in a class. Some operators rely on context. Context in this case really means whatever window/area you called the operator in. So some code just won’t work when called from the text editor.