5
$\begingroup$

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)
bpy.context.scene.objects.link(object)
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.

$\endgroup$
  • 1
    $\begingroup$ possible duplicate of poll() failed, context incorrect? - Example: bpy.ops.view3d.background_image_add() $\endgroup$ – Chebhou May 17 '15 at 10:42
  • $\begingroup$ 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. $\endgroup$ – zeffii May 17 '15 at 10:50
  • $\begingroup$ 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) $\endgroup$ – zeffii May 17 '15 at 13:17
4
$\begingroup$

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.

enter image description here

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

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

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

mesh in wireframe showing the modifier settings mesh in solid view showing the modifier settings

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.