Extrude Along Curve

Hi All,

Here is another addon I wrote to use BMesh. This allows you to extrude a mesh face along a bezier spline with options to rotate and scale the extrusion as well as change the number of extrusion steps.

To use the script either paste the code into the text editor and run script, or save as a .py and install as an addon. Then follow these steps;


  • Ensure you have a bezier curve object in your scene
  • Select the face to extrude in the mesh in edit mode.
  • In the spacebar menu search for “Extrude Along Curve” and select “Extrude Along Curve”.
  • Set the operator properties as you require, if your curve object contains more than one spline, you can select which using the Spline Index property.

Here’s an example using just a cube and a single bezier curve.

You can download the Py script at the link below:
Download Py

#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

# <pep8 compliant>

bl_info = {
    "name": "Extrude Along Curve",
    "author": "Andrew Hale (TrumanBlending)",
    "version": (0, 1),
    "blender": (2, 6, 3),
    "location": "",
    "description": "Extrude a face along a Bezier Curve",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Mesh"}

import bpy
import bmesh
from mathutils import Vector, Quaternion
from math import ceil, floor, pi

def eval_bez_tan(mat, points, t):
    num = len(points)
    t *= num - 1
    upper = ceil(t)
    lower = floor(t)
    if upper == lower:
        if upper == 0:
            return (mat * (points[upper].handle_right - points[upper].co)).normalized()
        elif upper == num - 1:
            return (mat * (points[upper].co - points[upper].handle_left)).normalized()
            return (mat * (points[upper].co - points[upper].handle_left)).normalized()
        t -= lower
        pupper = points[upper]
        plower = points[lower]
        tangent = -3 * (1 - t) ** 2 * plower.co + (-6 * (1 - t) * t + 3 * (1 - t) ** 2) * plower.handle_right + (-3 * t ** 2 + 3 * (1 - t) * 2 * t) * pupper.handle_left + 3 * t ** 2 * pupper.co
        tangent = mat * tangent
        return tangent

def eval_bez(mat, points, t):
    num = len(points)
    t *= num - 1
    upper = ceil(t)
    lower = floor(t)
    if upper == lower:
        return mat * points[upper].co
        t -= lower
        pupper = points[upper]
        plower = points[lower]
        pos = (1 - t) ** 3 * plower.co + 3 * (1 - t) ** 2 * t * plower.handle_right + 3 * (1 - t) * t ** 2 * pupper.handle_left + t ** 3 * pupper.co
        return mat * pos

def curve_ob_enum(self, context):
    obs = context.scene.objects
    cuobs = [(str(i), ob.name, ob.name) for i, ob in enumerate(obs) if ob.type == 'CURVE']
    curve_ob_enum.temp = cuobs
    return cuobs

class ExtrudeAlongCurve(bpy.types.Operator):
    bl_idname = "mesh.extrude_along_curve"
    bl_label = "Extrude Along Curve"
    bl_options = {'REGISTER', 'UNDO'}

    resolution = bpy.props.IntProperty(name="Resolution", default=1, min=1, soft_max=100)
    scale = bpy.props.FloatProperty(name="Scale", default=1.0, soft_min=0.0, soft_max=5.0)
    rotation = bpy.props.FloatProperty(name="Rotation", default=0.0, soft_min=-2 * pi, soft_max=2 * pi, subtype='ANGLE')
    splineidx = bpy.props.IntProperty(name="Spline Index", default=0, min=0)
    snapto = bpy.props.BoolProperty(name="Snap To Face", default=True)
    curveob = bpy.props.EnumProperty(name="Curve", items=curve_ob_enum)

    def poll(self, context):
        ob = context.active_object
        for cuob in context.scene.objects:
            if cuob.type == 'CURVE':
            return False

        return (ob is not None) and (ob.type == 'MESH') and (context.mode == 'EDIT_MESH')

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "curveob", text="", icon='CURVE_DATA')
        layout.prop(self, "resolution")
        layout.prop(self, "scale")
        layout.prop(self, "rotation")
        layout.prop(self, "splineidx")
        layout.prop(self, "snapto")

    def execute(self, context):
        ob = bpy.context.active_object
        me = ob.data
        bm = bmesh.from_edit_mesh(me)

        # Get the selected curve object and the required spline
        cuob = context.scene.objects[int(self.curveob)]
        cu = cuob.data

        self.splineidx = min(self.splineidx, len(cu.splines) - 1)
        p = cu.splines[self.splineidx].bezier_points

        # Get the property values
        res = self.resolution
        scale = self.scale
        rotation = self.rotation
        dscale = (1 - scale) / res
        drot = rotation / res

        # Get the matrices to convert between spaces
        cmat = ob.matrix_world.inverted() * cuob.matrix_world
        ctanmat = cmat.to_3x3().inverted().transposed()

        # The list of parameter values to evaluate the bezier curve at
        tvals = [t / res for t in range(res + 1)]

        # Get the first selected face, if none, cancel
        for f in bm.faces:
            if f.select:
            return {'CANCELLED'}

        # Get the position vecs on the curve and tangent values
        bezval = [eval_bez(cmat, p, t) for t in tvals]
        beztan = [eval_bez_tan(ctanmat, p, t) for t in tvals]
        bezquat = [0] * len(tvals)

        # Using curve only
        bezquat[0] = beztan[0].to_track_quat('Z', 'Y')
        fquat = bezquat[0].inverted()

        # Calculate the min twist orientations
        for i in range(1, res + 1):
            ang = beztan[i - 1].angle(beztan[i], 0.0)
            if ang > 0.0:
                axis = beztan[i - 1].cross(beztan[i])
                q = Quaternion(axis, ang)
                bezquat[i] = q * bezquat[i - 1]
                bezquat[i] = bezquat[i - 1].copy()

        # Get the faces to be modified
        fprev = f
        no = f.normal.copy()
        faces = [f.copy() for i in range(res)]

        # Offset if we need to snap to the face
        offset = Vector() if not self.snapto else (f.calc_center_median() - bezval[0])

        # For each of the faces created, set their vert positions and create side faces
        for i, data in enumerate(zip(faces, bezval[1:], bezquat[1:])):

            fn, pos, quat = data
            cen = fn.calc_center_median()

            rotquat = Quaternion((0, 0, 1), i * drot)

            for v in fn.verts:
                v.co = quat * rotquat * fquat * (v.co - cen) * (1 - (i + 1) * dscale) + pos + offset

            for ll, ul in zip(fprev.loops, fn.loops):
                ff = bm.faces.new((ll.vert, ll.link_loop_next.vert, ul.link_loop_next.vert, ul.vert))

            fprev = fn


        return {'FINISHED'}

def register():

def unregister():

if __name__ == "__main__":

Hope this script comes in useful.



FOR SURE! thank you very much!

I think I may be missing a step when trying to paste code into a text editor and saving as a .py file. I tried this once using the vertex chamfer addon. I pasted the code into notepad and saved as a .py file. Then when I went to Blender and clicked the install add-on button, the .py file I just created wouldn’t show up in the list of choices. Any idea what I’m doing wrong?

This looks awesome by the way!!


Hi ifcruickshank,

I’ve added a .py file download to hopefully fix some problems. If that fails, try pasting directly into Blender’s text editor and saving from there.


You are the man. Gotta try this now.

Thank you! I’ve never used Blender’s text editor before, but I’ll make it a point to take a look at it.

Cool addon! You should keep writing these kind of small but super-useful scripts!


Oh, this is awesome. I didn’t got it working at first - RTFM, hehe - select curve, then object & enter edit mode… Got it now & works like a charm! :slight_smile:

This is one of those (‘few’) functions (hehe) from Maya I miss in Blender. Awesomeness! Tnx! :smiley:

Thanks Truman!

If it would let live editing of the curve, matching the extrusion in real-time, it would win the best-addon-of-the-year award!

Ngons are really useful to use this addon. Can’t do this without Ngons. Thanks again, keep rockin’



Yeah, hehe, by some reason I also got the idea for horns… ;D

@Farm - lol yea, horns ftw! The “taper” function is a nice feature, good work Truman!

What you’re gonna code next time? :smiley: Have you any idea? You seem on your right way at this.

Kind regards

“Sweep like” use, it gives a nice and smooth polygon flow, depending on your curve. Look:


Yo all,

@TrumanBlending i just like you :wink:
Thank you so much for this great addon very useful as usual , i will try now
Thank you.

Um this is great and all but isn’t this the same as doing bevel/taper with curves ?

really usefullllll ! thanks truman

Thanks for all your efforts Truman.

But, I have a problem. I can’t use this add-on with my latest win build. There are always errors. I think I didn’t understand how to use it. I select the curve first and then shift select the cube. What’s wrong here? Look at the video below.


@demohero - i think it does not work with paths, just Bezier…not sure about it. Did u try with Bezier?


That’s not a bezier curve though, or am I missing something?

Edit: No, doesn’t work with path.

I am sure, tried it - it doesn’t work with a path, you need to use bezier. :slight_smile:

Thanks for the solutions guys. Yes, it only works with bezier, not others.

Why not? :slight_smile: