# Spin mesh (to create solid of revolution)

I want to be able to take a function f(x) and first make a plot of the function on given interval and then build a solid of revolution of that mesh by rotating it around X-axis of the plot.

I've made a script that generates the graph. My question is how to spin the mesh (360° angle).

I tried to use function bpy.ops.mesh.spin:

bpy.ops.mesh.spin(steps=36, angle=2*pi, axis=(1, 0, 0))


but I still get RuntimeError

Traceback (most recent call last):
File "<blender_console>", line 1, in <module>
File "C:\Program Files\Blender Foundation\Blender\2.72\scripts\modules\bpy\ops.py", line 188, in __call__
ret = op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.mesh.spin.poll() failed, context is incorrect


My code for generating graph mesh of given function:

# User function (return None if undefined)
def user_function(x):
"Math function for given X, returns Y"
if x == 0:
return None
return 1/x

# Resolution - number of samples
res = 100

# MIN and MAX values for the graph to plot
minX = 1
maxX = 10
minY = 0
maxY = 1

# Real size of a rectangle of the graph
sizeX = 2
sizeY = 2

# Recalculate Y-value for undefined points
recalculate_undefined = True

# Whether to delete old object
delete_old = True

# ===== Program =====
import bpy

step = (maxX - minX) / (res - 1)

coefX = sizeX / (maxX - minX)
coefY = sizeY / (maxY - minY)

RminX = - sizeX / 2
RminY = - coefY * minY

Z = 0

vertices = []
undefined = []
# Calculate values
for ix in range(res):
x = minX + step * ix

# Calculate and store Y-value
y = user_function(x)
if y == None:
undefined.append(ix)
y = 0
y = min(maxY, y)
y = max(minY, y)

cX = step * ix * coefX + RminX
cY = y * coefY + RminY
cZ = Z
vertices.append((cX, cY, cZ))

# Calculate undefined points
if recalculate_undefined == True:
for ix in undefined:
avg = []
if ix > 0 and (ix - 1) not in undefined:
avg.append(ix - 1)
if ix < (res - 1) and (ix + 1) not in undefined:
avg.append(ix + 1)

if len(avg) >= 1:
y = 0
for a in avg:
y += vertices[a][1]
y /= len(avg)
vertices[ix] = (vertices[ix][0], y, Z)

# Delete old object
if delete_old == True:
if "Graph" in bpy.data.objects:
bpy.ops.object.select_all(action = 'DESELECT')
bpy.data.objects["Graph"].select = True
bpy.ops.object.delete()
for item in bpy.data.meshes:
if item.users == 0:
bpy.data.meshes.remove(item)

# Create new mesh
mesh = bpy.data.meshes.new("graph")

# Build edges from vertices
edges = [(ix, ix + 1) for ix in range(res - 1)]
faces = []

# Complete mesh and build object
mesh.from_pydata(vertices, edges, faces)
object = bpy.data.objects.new("Graph", mesh)
object.location = bpy.context.scene.cursor_location


Edit1:

This post is not a duplicate of poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add().

I'm looking for a solution without only avoiding the error by changing contexts.

• possible duplicate of poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add() – Chebhou May 17 '15 at 10:42
• If we focus on what you want, which is a spin or geometry Lathe, then this is not a duplicate. The error however can be avoided by changing contexts. (linked above) . Below I've answered a way to do this without the context. – zeffii May 17 '15 at 10:50
• Although the specific context override for this question can be found in the edited Question at the bottom blender.stackexchange.com/questions/7205/… . I don't think this is a duplicate because the question is how to spin geometry not how to override (even though that's the error that led to the question) – zeffii May 17 '15 at 13:17

You could use the spin provided by bmesh operators. This doesn't require any particular context. Here's an example:

From sverchok source code:

import bpy
import bmesh
from bmesh.ops import spin
import math

def lathe_geometry(bm, cent, axis, dvec, angle, steps, remove_doubles=True, dist=0.0001):
geom = bm.verts[:] + bm.edges[:]

# super verbose explanation.
spin(
bm,
geom=geom,         # geometry to use for the spin
cent=cent,         # center point of the spin world
axis=axis,         # axis, a (x, y, z) spin axis
dvec=dvec,         # offset for the center point
angle=angle,       # how much of the unit circle to rotate around
steps=steps,       # spin subdivision level
use_duplicate=0)   # include existing geometry in returned content

if remove_doubles:
bmesh.ops.remove_doubles(bm, verts=bm.verts[:], dist=dist)

obj = bpy.data.objects['Graph']
bm = bmesh.new()
bm.from_mesh(obj.data)

axis = (1,0,0)
dvec = (0,0,0)
angle = 2*math.pi
steps = 20
cent = obj.location

lathe_geometry(bm, cent, axis, dvec, angle, steps, remove_doubles=True, dist=0.0001)

bm.to_mesh(obj.data)
# obj.data.update()   # if you want update to show immediately
bm.free()


Things to be aware of:

• A small update in 3d view is required to see the result. The mesh will get updated whenever Blender call updates, so you don't strictly need to force the update yourself, but you can if you want to by called obj.data.update()
• Possible downside of the bmesh.spin is that the order/topology of faces is not guaranteed or predictable.

You might prefer to use a further abstraction, which assumes the center of the rotation is obj.location: https://gist.github.com/zeffii/d4efc7c346d6aa90ae18

• It seems to be working like expected. Thanks! – jjurm May 17 '15 at 11:21

You could also use the 'screw' modifier, like this :)

• +1 for tricky solution :) works with angle 360° and screw offset set to 0. – jjurm May 17 '15 at 11:25