3
$\begingroup$

I have a NURBS curve which has some thickness (since I set the Bevel Object to be a circle).

enter image description here

I have a list of RGB values specifying what color the curve should be at each point along the NURBS curve.

How can I achieve this with Python? It may have been easy if NURBS curves allowed vertex painting, but it appears that they don't.

$\endgroup$
3
$\begingroup$

I figured out a way to do it.

I created a script that first remembers all the locations of the control points of the NURBS curve (each of which has a color associated with it in a separate variable). Then I converted the curve to a mesh.

Next, for each vertex of the mesh, I loop through all the original NURBS control points, see which one is closest, and then color the vertex of the mesh with the corresponding color.

Here is the result:

colored NURBS curve

It's not perfect, it would be better if the colors slowly blended into each other, but if you have many control points, it might be OK.

I'll include the script I used in case anyone finds it useful:

import bpy
from mathutils import Vector
import copy


def color_nurbs_curve(nurbs_obj, color_list):
    """Converts the NURBS curve which should already have a bevel object so it has some width.

    color_list should be a list of colors each of which is a 3 element list with [R,G,B]."""

    nurbs_coords = copy.deepcopy( [ nurbs_obj.data.splines[0].points[i].co for i in range(len(nurbs_obj.data.splines[0].points)) ] )

    #Error checking
    if len(nurbs_coords) != len(color_list):
        raise RuntimeError('The color list must have a number of colors equal to the number of control points of your bezier curve')
    if nurbs_obj.data.bevel_object == None:
        raise RuntimeError('Your NURBS curve must have a bevel object')

    scn = bpy.context.scene




    #Convert to mesh
    scn.objects.active = nurbs_obj
    nurbs_obj.select = True
    bpy.ops.object.convert( target='MESH' )
    #sometimes this conversion results in a bunch of doubles, so I remove the doubles
    remove_doubles(nurbs_obj)

    mesh = nurbs_obj.data 

    if len(mesh.vertex_colors) == 0:
        bpy.ops.mesh.vertex_color_add()

    nurbs_obj.active_material = bpy.data.materials.new('material')
    nurbs_obj.active_material.use_vertex_color_paint = True


    print("test",nurbs_coords[1])

    #loop through each vertex
    num_verts = len(mesh.vertices)
    for vert_i in range(num_verts):

        #record shortest separation. -99 signals unset.
        shortest_sep = -99

        #loop through all the original bezier points to see 
        #which one is closest and then color it with the corresponding color
        count=0
        for b_point in nurbs_coords:

            b_point.resize_3d()
            temp_sep = (mesh.vertices[vert_i].co -  b_point).length

            if temp_sep < shortest_sep or shortest_sep == -99:
                shortest_sep = temp_sep
                color = color_list[count]

            count += 1

        color_vertex( nurbs_obj, vert_i, color )
        print( "Finished vertex: " + str(vert_i) + "/" + str(num_verts) )





def color_vertex(obj, vert, color=[1,0,0]):
    """
    Paints a single vertex

    """
    mesh = obj.data 
    scn = bpy.context.scene

    #check if our mesh already has Vertex Colors, and if not add some... (first we need to make sure it's the active object)
    scn.objects.active = obj
    obj.select = True
    if len(mesh.vertex_colors) == 0:
        bpy.ops.mesh.vertex_color_add()

    i=0
    for poly in mesh.polygons:
        for vert_side in poly.loop_indices:
            global_vert_num = poly.vertices[vert_side-min(poly.loop_indices)] 
            if vert == global_vert_num:
                mesh.vertex_colors[0].data[i].color = color

            i += 1

def remove_doubles(obj):
    """ Removes doubles using default settings"""
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='TOGGLE') #I believe objects are created with the vertices all not selected
    bpy.ops.mesh.remove_doubles()
    bpy.ops.object.mode_set(mode='OBJECT')



##Example Usage    
##nurbs_obj = bpy.data.objects['NurbsCurve']
##color_list = [  [1.0, 0.0, 0.0], [1.0, 0.25, 0.0], [1.0, 0.5, 0.0], [0.5, 0.0, 0.0], [0.0, 0.75, 0.0], [0.0, 1.0, 0.0], [0.5, 0.5, 0.5], [0.75, 0.15, 0.9]   ]
##color_nurbs_curve(nurbs_obj, color_list)
$\endgroup$
  • 1
    $\begingroup$ BTW, I'd be interested in hearing more ideas for solving this problem. $\endgroup$ – Garrett Jan 10 '14 at 8:22
0
$\begingroup$

You could use a Colour Ramp to get a variation of colour along a curve but I don't see an easy way to match a point on a curve to a point in the ramp.

I think you may want to create a mesh cylinder and define the colours on it at the locations you want and then use a curve modifier to deform the mesh to the shape you want.

Also the python to create the material will vary depending on whether you are going to use blender internal or cycles.

$\endgroup$
  • $\begingroup$ Good ideas, I wasn't able to get them working but it seems like they should work. I found another way, which I'll post. $\endgroup$ – Garrett Jan 10 '14 at 4:58

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.