I want to use Blender and bpy to create sinusoidal struts. I have been using Python with VTK in the past, but it is causing some trouble and I feel Blender might be the better solution.

An example of what I want to create is depicted below. It is planar in a sense that only one axis is modified with a sin (or in this case cos) function. It is also tilted by 45°, but that's not the big hurdle.


Since I am very new to Blender, I googled around a bit and found this: http://wiki.theprovingground.org/blender-py-mathmesh

It contains the following code that creates a surface that is modified with sin:

import bpy
import math

# mesh arrays
verts = []
faces = []

# mesh variables
numX = 10
numY = 10

# wave variables
freq = 1
amp = 1
scale = 1

#fill verts array
for i in range (0, numX):
    for j in range(0,numY):

        x = scale * i
        y = scale * j
        z = scale*((amp*math.cos(i*freq))+(amp*math.sin(j*freq)))

        vert = (x,y,z) 

#fill faces array
count = 0
for i in range (0, numY *(numX-1)):
    if count < numY-1:
        A = i
        B = i+1
        C = (i+numY)+1
        D = (i+numY)

        face = (A,B,C,D)
        count = count + 1
        count = 0

#create mesh and object
mesh = bpy.data.meshes.new("wave")
object = bpy.data.objects.new("wave",mesh)

#set mesh location
object.location = bpy.context.scene.cursor_location

#create mesh from python data

I just can't rewrite it to just creating a strut, though. Originally, I created the points using the following approach with numpy + vtk, but when I try this in Blender I get error messages ('Unsupported operand type(s) for /: "tuple" and "int"' on the line where I define y in the function).

def strut(x, node_dist=5, amp=1):
    """Takes 1D array x, creates shape and normalizes it to fit the length of the cosine function."""
    x = x * 2 / node_dist * np.pi  # normalize length to fit [-4pi; 4pi]
    y = amp / 2 * np.cos(x) + amp / 2  # adjusting position and amplitude
    return y

a = np.linspace(-2 * node_dist, 2 * node_dist, points_per_line)
b = strut(a, node_dist, amp) - node_dist / 2.
c = strut(a, node_dist, amp) + node_dist / 2.

for i in range(0, a.shape[0], 1):
    if strut_number == 1:
        x = a[i]
        y = b[i]
        z = c[i]

If I could just get some help with being able to define how many points on the line I want(-> smoothness) and how to define the shape (ideally quadratic) around the strut I could figure the rest out myself. I hope my question is specific enough and not too odd. Thanks for any help!

  • $\begingroup$ It seems that you have a bug in the face-generating logic. The way you allocate verts to faces, you assign indices that do not exist (beyond the max index). Also, the result of the first piece of code is a surface that looks like a wave, not a strut. When I ran the strut code function, I did get something that looks more like a strut but it was only a line of verts rather than a complete 3D surface. $\endgroup$
    – TLousky
    Jul 12, 2017 at 14:10
  • $\begingroup$ @TLousky Yeah I it is faulty at this state. I just thought the surface is a more complex version of what I want and I can "dumb it down" to a line/strut. How did you get my function to work with bpy? Did you not get the error I got? $\endgroup$
    – Ian
    Jul 12, 2017 at 14:33

1 Answer 1


Got it to work with Bmesh (not necessary but easier to work with IMHO). Had to add several chunks of code that will generate 4 waves as the main longitudinal edge loops, and add faces to the mesh.

enter image description here

This can be fairly easily expanded to generate other shapes, or non-quad based struts. Here you can control the width (thickness) of the 3D strut via the width global and zw / xw variables within the loop.

I also changed the strut function a tiny bit. Instead of normalizing between -4 to +4 X pi, I just use the Y values provided. In this example I'm providing the minimum and maximum Y values as multiples of pi directly, so I removed the normalization to open up the possibility to create a wave in whichever length and number of peaks you want.

Here's the code:

import bpy, bmesh, math
from mathutils import Vector
import numpy as np

numPoints  = 100
yMin       = -3 * np.pi 
yMax       = 1 * np.pi
width      = 0.5

def strut( x, node_dist = 5, amp = 1 ):
    """ Takes 1D array x, creates shape y values """
    return amp / 2 * np.cos(x) + amp / 2  # adjusting position and amplitude

bm    = bmesh.new() # Generate bmesh object
loops = []          # Strut major longitudinal loops array

# Calculate vertex coordinates
# Essentially generate 4 longitudinal loops that represent the top, bottom. left and right edge loops of the strut
y = np.linspace( yMin, yMax, numPoints )
for zw, xw in zip( [1, 0, -1, 0], [0, -1, 0, 1] ):
    z = strut( y, amp = 2 ) + zw * width
    x = strut( y, amp = 0 ) + xw * width

    loop = [ 
        bm.verts.new( Vector(( xi, yi, zi )) ) 
        for xi, yi, zi in zip( x, y, z ) 

    loops.append( loop )

# Add faces
for i in range( len( loops ) ):
    for j in range( len( loops[i] ) - 1 ):
        verts = []
        if i == len( loops ) - 1:
            verts.append( loops[0][j] )
            verts.append( loops[0][j+1] )
            verts.append( loops[i][j + 1] )
            verts.append( loops[i][j] )
            verts.append( loops[i][j] )
            verts.append( loops[i][j + 1] )
            verts.append( loops[i+1][j+1] )
            verts.append( loops[i+1][j] )


# Add caps faces
for i in [0, len( loops[0] ) - 1 ]:
    bm.faces.new([ loops[j][i] for j in range(len(loops)) ])

# Generate mesh data and object, and link to scene
m = bpy.data.meshes.new('strut')

o = bpy.data.objects.new('strut', m)
bpy.context.scene.objects.link( o )

enter image description here

  • 1
    $\begingroup$ This is amazing, thank you so much! I wish I could upvote more than once :) $\endgroup$
    – Ian
    Jul 20, 2017 at 12:57

You must log in to answer this question.

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