# How to create cylinder with varying radius at different height

My goal is to create a imperfect cylinder with a script, where some randomness is added to the radius of the cylinder at certain intervals. I am very new to blender unfortunately, and am lost at the approach to take.

I would imagine I would need to generate circle of vertices, connect them with edges, duplicate and extrude, move points and repeat. However, I don't have the faintest idea of the commends I would need for each step, and would appreciate any help/hints.

• Is there a reason you need to do this with a script, rather than say a cylinder with a displacement modifier based on a noise texture? If you’re just doing this to learn scripting then of course that makes sense, but otherwise a script seems overkill for this task. – Alexis King Feb 23 at 5:25
• It's part of an attempt to generate a synthetic dataset, something that would be repeated thousands of times. – Ekiden Feb 23 at 11:48

## 1 Answer

Bmesh script. Here is but one of may ways could approach this.

Rib n Bridge

• Make growth_segments cirlcles all with segments segments. Each has a radius in range (min_radius, max_radius) and the the next is in the range of (min_growth, max_growth) above.
• A translation matrix, using the accumulative height is used to place each successive circle.
• Each circle is added with bmesh's create circle primitive. On each iteration a "rib" is the last added segments edges of the bmesh.
• Skinned by bridging each rib with its next.
• Finally the first and last ribs are ngon filled with the contextual_create operator, somewhat akin to bmesh's F "fill" key.

import bpy
from bpy import context # for testing
import bmesh
from random import uniform
from mathutils import Matrix

segments = 16
min_radius = 0.5
max_radius = 2.0

growth_segments = 10
min_growth = 1.0
max_growth = 2.0

bm = bmesh.new()

h = 0
ribs = []
# make the circles
for i in range(growth_segments):
bmesh.ops.create_circle(
bm,
segments=segments,
radius=uniform(min_radius, max_radius),
matrix=Matrix.Translation((0, 0, h)),
)
h += uniform(min_growth, max_growth)
ribs.append(bm.edges[-segments:])

# bridge the loops
for rib, ribnext in zip(ribs, ribs[1:]):
bmesh.ops.bridge_loops(
bm,
edges=rib + ribnext,
)

# fill the ends

bmesh.ops.contextual_create(
bm,
geom=ribs + ribs[-1],
)

# Create object load mesh
me = bpy.data.meshes.new("RandomCyl")
bm.to_mesh(me)
ob = bpy.data.objects.new("RandomCyl", me)
context.collection.objects.link(ob)
# make selected and active
ob.select_set(True)
context.view_layer.objects.active = ob


Matrices

Have used a matrix in the create circle operator to translate to the height Here I've extended this a bit further to give a panel beaten effect, each rib is now a random ellipse using above min max for two radii, and also rotated randomly 0 to 45 degrees in Z.

A diagonal matrix is a quick way to produce a scale matrix.

Sx = Matrix.Scale(4, 3, 'X')


is equiv of

S = Matrix.Diagonal((4, 1, 1))


Each circle is transformed by R @ T @ S (rotation, translation, scale)

    bmesh.ops.create_circle(
bm,
segments=segments,
radius=1,
matrix=(
Matrix.Rotation(uniform(0, 0.7854), 4, 'Z') @
Matrix.Translation((0, 0, h)) @
Matrix.Diagonal(
(
uniform(min_radius, max_radius),
uniform(min_radius, max_radius),
1,
)
).to_4x4() # since tanslation mat is 4x4
),
)


Cannot emphasize enough, the better we become at using linear algebra (vectors and matrices) the "easier" it will be to use API methods. Is also generally faster and IMO cleaner.

Spin Profile

Another way would be to make profile edges on XZ plane and spin it around z axis to make a solid of revolution. Equivalent of making profile shown on left, and creating mesh by spinning it with the screw modifier (default settings)

import bpy
from bpy import context # for testing
import bmesh
from random import uniform
from mathutils import Matrix
from math import radians

segments = 16
min_radius = 0.5
max_radius = 2.0

growth_segments = 10
min_growth = 1.0
max_growth = 2.0

bm = bmesh.new()

h = 0
verts = [bm.verts.new()]

for h in range(growth_segments):
radius = uniform(min_radius, max_radius)
verts.append(
bm.verts.new((radius, 0, h))
)
h += uniform(min_growth, max_growth)

verts.append(bm.verts.new((0, 0, verts[-1].co.z)))

for edge in zip(verts, verts[1:]):
bm.edges.new(edge)

# spin the sucker

bmesh.ops.spin(
bm,
geom=bm.edges[:],
axis=(0, 0, 1), # z axis
angle=radians(360), # 2pi one rev
steps=segments,
)