2
$\begingroup$

I've created a mesh object by generating two Bezier curves and converting that to a mesh using this script

for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)
r = 16
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 0.0, 0.0), enter_editmode = 1, align = 'WORLD')
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 1.0, 0.0), radius=1.0)
bpy.context.object.data.resolution_u = r
bpy.ops.curve.de_select_last( )
bpy.ops.object.editmode_toggle()
bpy.ops.object.convert(target='MESH')

Doing so produces this object:

An object consisting of two loops created from Bezier Curves

How would I march across the loops, selecting vertices in a loop, first the first 4, create a face, and so on, following the loop?

$\endgroup$
5
  • $\begingroup$ I need this to happen automatically in a loop. Ultimately, you need the vertices to merge into polygons. $\endgroup$
    – 10 10
    Oct 13, 2021 at 16:11
  • $\begingroup$ or at least not select but just create polygons $\endgroup$
    – 10 10
    Oct 13, 2021 at 16:14
  • $\begingroup$ Here is the program code: $\endgroup$
    – 10 10
    Oct 13, 2021 at 16:24
  • $\begingroup$ import bpy for obj in bpy.data.objects: bpy.data.objects.remove(obj) #clean scene r = 16 bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 0.0, 0.0), enter_editmode = 1, align = 'WORLD') bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 1.0, 0.0), radius=1.0) bpy.context.object.data.resolution_u = r bpy.ops.curve.de_select_last( ) bpy.ops.object.editmode_toggle() bpy.ops.object.convert(target='MESH') bpy.ops.object.editmode_toggle() $\endgroup$
    – 10 10
    Oct 13, 2021 at 16:25
  • $\begingroup$ i just create curves and convert them to mesh. I just need to merge the vertices $\endgroup$
    – 10 10
    Oct 13, 2021 at 16:25

2 Answers 2

4
$\begingroup$

XY Problem

The desired result and the question title IMO make this a bit of an XY Problem In that, as I see it, to get the result requires neither vertex indices, nor edit mode.

Firstly as mentioned (early) in @MartyFouts answer could call the bridge edge loops operator, while in mesh edit mode with all verts (edges) selected.

The verts have a creation order, using indices.

enter image description here Showing vert indices, via overlay

As mentioned in RobinBetts answer to

How can I sort vertex positions sequentially indices in a closed area?

it is more than likely the case, after converting curve to mesh the vertices are in order. As shown in image 0 -> 17 on first spline, 18 -> 33 on second. To make use of this

import bmesh
import bpy
from bpy import context

r = 16
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 0.0, 0.0), enter_editmode = 1, align = 'WORLD')
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 1.0, 0.0), radius=1.0)
ob = context.object
ob.data.resolution_u = r
bpy.ops.curve.de_select_last( )
bpy.ops.object.editmode_toggle()
bpy.ops.object.convert(target='MESH')

# bmesh bit.

me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
bm.verts.ensure_lookup_table()
# edges (0, 17), (1, 18), ... (i, i + r + 1)..
edges = [(i, i + r + 1) for i in range(r + 1)]
# faces (0, 17, 18, 1), ... walk around CCW order.
for e0, e1 in zip(edges, edges[1:]):
    bm.faces.new(bm.verts[i] for i in e0 + tuple(reversed(e1)))
bm.to_mesh(me)

Without indices or toggling in out of edit mode.

As a "rule of thumb" I avoid edit mode and indices to create meshes if possible. Another way to look at this is via

Extrusion.

Can I extrude a path in x/y

Big fan of using bmesh instead of bpy.ops.mesh to manipulate a mesh. No doubt a single bridge edge loops operator call is "easiest" in this instance, but as our destination becomes more complex, toggling modes, and setting selections to use operators (IMO) becomes tiresome (and slow if looping operator calls) quickly.

To produce same result, will add the first bezier curve as in question.

Rather than adding an offset second bezier curve and filling, will instead extrude all the edges of the original, to the location second.

Bmesh operators return a result in a dictionary. How to select extruded vertices in bmesh?

Test Script, Run in OBJECT mode.

Create first curve and convert, extrude all edges via bmesh op, then translate the extrusion created verts (all returned geometry of type bmesh.types.BMVert) to the location of the second curve. (Image above also shows result of script below, in edit mode)

import bpy
import bmesh
from bmesh.types import BMVert

context = bpy.context
# first curve as in question.
r = 16
bpy.ops.curve.primitive_bezier_curve_add(
        location=(1.0, 0.0, 0.0),
        align = 'WORLD'
        )
ob = context.object
ob.data.resolution_u = r
bpy.ops.object.convert(target='MESH')
# bmesh extrude'n'move
me = ob.data
bm = bmesh.new()
bm.from_mesh(me)
# extrude the edges
ret = bmesh.ops.extrude_edge_only(
        bm,
        edges=bm.edges,
        )['geom']
        
bmesh.ops.translate(
        bm,
        verts=[e for e in ret if isinstance(e, BMVert)],
        vec=(0, 1, 0),
        )        

bm.to_mesh(me)
me.update()

Related.

How can I create a wall(mesh) from 2 profiles and a path that connects them?

Lofting: can I copy a Shape-Keyed mesh for each frame of its animation?

$\endgroup$
1
$\begingroup$

You are creating your curves using this script that you mentioned in the comments on your question:

for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)
r = 16
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 0.0, 0.0), enter_editmode = 1, align = 'WORLD')
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 1.0, 0.0), radius=1.0)
bpy.context.object.data.resolution_u = r
bpy.ops.curve.de_select_last( )
bpy.ops.object.editmode_toggle()
bpy.ops.object.convert(target='MESH')

if you add two lines to the bottom of the script, bpy ops will bridge the two edge loops you just created:

bpy.ops.mesh.select_all()
bpy.ops.mesh.bridge_edge_loops()

If you want to access individual vertices, instead of creating the faces, then one method is to create a bmesh from the mesh. Instead of the above two lines,

obj = bpy.context.active_object
mesh = obj.data
bpy.ops.object.editmode_toggle()
bm = bmesh.from_edit_mesh(mesh)
bm.verts.ensure_lookup_table()

creates the bmesh and you can find vertices from their indices via code like

v1, v2 = bm.verts[0], bm.verts[13]

Here is a script that only assumes that there are two edge chains but that there's nothing known about their index ordering. It fills in the faces in the bmesh, before replacing the original script with it.

It's somewhat verbose to make it clear what it is doing. Hopefully the comments are sufficient to explain how it works

import bpy
import bmesh


#
# Original script from question.
# Tries to remove all of the objects from the scene
# Creates two parallel Bezeir curves in the same Bezier curve object
# Converts the Bezier curve object to a mesh object
# Leaves the object selected in Object Mode.

for obj in bpy.data.objects:
    bpy.data.objects.remove(obj)

r = 16

bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 0.0, 0.0), enter_editmode = 1, align = 'WORLD')
bpy.ops.curve.primitive_bezier_curve_add(location=(1.0, 1.0, 0.0), radius=1.0)
bpy.context.object.data.resolution_u = r
bpy.ops.curve.de_select_last( )
bpy.ops.object.editmode_toggle()
bpy.ops.object.convert(target='MESH') 

# The easy way to make faces between the two curves
#bpy.ops.object.editmode_toggle() 
#bpy.ops.mesh.select_all()

#-----------------------------------------------------------------------------

# Find the mesh we just created.  Since there's only one object in the scene
# and it's the active object, we use the simple way.
obj = bpy.context.active_object
mesh = obj.data

# bmesh ops are done in edit mode.
bpy.ops.object.editmode_toggle()

# Make a bmesh from the mesh and validate its lookup tables.
bm = bmesh.from_edit_mesh(mesh)
bm.verts.ensure_lookup_table()
bm.edges.ensure_lookup_table()

# Create two dictionaries from the list of edges
# the first dictionary has an entry for every vertex that is the second
# (right) vertex of an edge whose value is the first (left) vertex of that edge.
# The second dictionary has an entry for every left vertex from an edge
# whose value is the right vertex of that edge.
visit_left = {}
visit_right = {}

for e in bm.edges:
    visit_left[e.verts[1]] = e.verts[0]
    visit_right[e.verts[0]] = e.verts[1]

# Because we created two chains of edges, there will be two vertices
# that have no entry in the visit_left directory. They are the starting vertex
# of each chain.  Let's find them by noticing the vertices that have no
# entry in the left dictionary
heads = []

for v in bm.verts:
    try:
        l = visit_left[v]
    except KeyError:
        heads.append(v)

# Starting from each head, we construct the chain by looking up
# in the right dictionary which vertex is to the right of the current vertex.
# We simply iterate until we reach a vertex that has no entry. It's
# the right end of the chain
visit_chains = []

for next_vertex in heads:
    chain = [next_vertex]
    while True:
        try:
            next_vertex = visit_right[next_vertex]
            chain.append(next_vertex)
        except KeyError:
            break
    visit_chains.append(chain)

# Sanity check, print out the chains
for chain in visit_chains:
    print(list(v.index for v in chain))

# Make the consecutive faces.
bottom = visit_chains[0]
top = visit_chains[1]

for i in range(len(bottom)-1):
    print(f'({bottom[i].index}, {bottom[i+1].index}), ({top[i].index}, {top[i+1].index})')
    v0 = bottom[i]
    v1 = bottom[i+1]
    v2 = top[i]
    v3 = top[i+1]
    bm.faces.new((v2, v3, v1, v0))
    
# Replace the original mesh with the bmesh.
bmesh.update_edit_mesh(mesh,True)
```
$\endgroup$

You must log in to answer this question.

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