13
$\begingroup$

For scientific visualisation, I'm using a lot of splines which are bevelled with an object (i.e. a hexagon). I control the level of details depending on the context.

Without losing the benefits of curves, I want to assign colours to the curve points so that the generated mesh will show these colours (with interpolation). Here's an image of what I would like to achieve. The image below is an example of the object I want to map colours to. Some splines have hundreds of points. Each point has a colour so the mesh has to show the "exact" colour at the proper location.

Example of desired curve mesh colouringTarget example

I browsed stack exchange for an answer and I also did try to answer my own question before posting it here, see below.

1. the naive way

I tried looping over the curve points but there's no colour property. Meh.

2. curve mesh data

So, this is about looping over generated mesh vertices and then calculating how close they are to a point in order to assign the proper vertex colour.

When I have a meshed curve in my viewport, I do a loop

for mesh in bpy.data.meshes:
    print(mesh)

And I get 0 as a result. But my mesh is there, I can see it in Blender.

3. weights to colours with Cycles

So far what I did was assigning the colour value to the weight property of curve points, like this simplified code:

    polyline.points.add(len(points) - 1)
    for i in range(len(points)):
        vertex = points[i]
        # x, y, z and weight
        polyline.points[i].co = (x, y, z, w)
        # radius of the point (only meaningful if a tube-like volume is drawn around it)
        polyline.points[i].radius = r

I thought there would be a way to then transfer the weight to vertex colouring of the curve mesh but I found none since I can't access the mesh data.

So I randomly tried to find a way to use weights with Cycles, maybe there is something that I can plug to the colour input of a standard material ? Didn't find any.

4. using BMesh ?

Bmesh doesn't support curves so it's a dead end here as well.

5. uvw mapping

As Chebhou wrote, I could use UVW mapping but I would like to see this in python. However, I'm asking for a way to match colours at every curve point. It seems that uvw mapping is non linear so how do I know to what coordinate in space (on the mesh) a color will be ?

UPDATE : there is a limit to how many gradient elements you can have in a color ramp, 32 colours max. I have splines with way more points than that. I need to find an alternative way of using UVW mapping.

$\endgroup$
7
$\begingroup$

Check use UV for mapping in the curve tab (this act as unwrapping the curve mesh ):

enter image description here

use the U input from the texture coordinates node to control the color using ColorRamp node along the curve :

enter image description here

render result

enter image description here


UPDATE It seems that the UV space is divided equally between the bezier points , and using this info we can use the ColorRamp to assign a color to each point using knowing its order in the spline :

  • the number of elements in the color ramp should be the same as the number of bezier points
  • distribute the color ramp elements evenly
  • the color of each bezier point is the color of its corresponding element in the color ramp

this is a curve with 3 splines each spline has each own material :

enter image description here

using the previous idea gives the following result( color-ramp interpolation is set to constant to show thee relation between elements and bezier points ) :

enter image description here

Scripting : the following code has been used to create the last image materials

assuming you have

  • a dictionary of points' colors
  • material slot for each spline -each material has a ramp node ( the material should be as the previously used one ^)

import bpy

curve = bpy.context.object

colors = {  0 : [(0,0,1,1), (0,1,1,1), (1,0,0,1), (1,0,1,1), (1,1,0,1), (1,0,1,1), (1,1,0,1)],   
            1 : [(0,1,0.3,1), (0,0,1,1), (1,0,0,1), (1,0,1,1), (1,1,0,1), (1,0,1,1), (1,1,0,1)],
            2 : [(1,0.4,1,1), (0.8,0,1,1), (0.5,0.5,0,1), (0.6,0,0.2,1), (1,1,0,1), (1,0,1,1), (1,1,0,1)]
         }

if curve.type == 'CURVE' :
    i = 0
    if len(curve.data.splines) <= len(colors) :
        for spline in curve.data.splines :
            spline.material_index = i
            color_ramp = curve.material_slots[spline.material_index].material.node_tree.nodes['ColorRamp'].color_ramp
            l = len(spline.bezier_points) -1

            for e in range(1,l) :   # if i>0  create new color elements
                color_ramp.elements.new(e/l)
                print(e/l)
                color_ramp.elements[e].color = colors[i][e]

            color_ramp.elements[0].color = colors[i][0]
            color_ramp.elements[-1].color = colors[i][0]

            i+=1
$\endgroup$
  • $\begingroup$ Thanks for the reply but I need more than that. Maybe the method is right for a start. In my scene I have splines with dozens, sometimes hundreds of points. I have colour values for the points, not for the uvw map. So how do I exactly map colour values at the curve points ? I'm interested in the python approach here since I need this to be automated. $\endgroup$ – nantille Apr 5 '15 at 21:50
  • $\begingroup$ @nantille do you have the distance between the points ( not straight , the length of the segment ) ? $\endgroup$ – Chebhou Apr 5 '15 at 21:59
  • $\begingroup$ I have a list of points so I can easily calculate the straight distance between p1-p2, then p2-p3, and so on. That would roughly give me the length of the curve. If we need to have the real curve length, then there must be a method somewhere that calculates this. I use bspline interpolation, sometimes poly which is some linear interp. I know that for bezier curves you can use mathutils.geometry.interpolate_bezier(). I would like to avoid using mesh duplicates (blenderscripting.blogspot.ch/2012/08/…) for this since it can be solved with math. $\endgroup$ – nantille Apr 6 '15 at 8:05
  • $\begingroup$ Ah ! Then it's easy, I can split the ramp in as many color points as there are on the curve, separating them by equal distance. UVW mapping will take care of the rest. But there's still a little issue. I mentioned b-spline curves, not bezier curves. Does this also work for nurbs ? If not, do you know a method so that I can give my points list and I get a list of bezier points from that ? The handles would need to be computed automatically. $\endgroup$ – nantille Apr 6 '15 at 11:47
  • $\begingroup$ replace bezier_points with points and see if it get the same results $\endgroup$ – Chebhou Apr 6 '15 at 12:44
0
$\begingroup$

I was struggling for a while on how to show on a curve some data (i.e some defined color at each vertex of the curve).

I finally found a solution. IIt's not complicated but require some scripting.

Instead of starting from a curve. Starts with a line :

me = bpy.data.meshes.new('mycurve_mesh')
verts = [x1, x2, x3...]
edges = []
for i in range(len(verts)-1) :
    edge.append([i,i-1]);
me.from_pydata(verts, edges,[])
ob = bpy.data.objects.new(me, 'mycurve')

Then put your vertex data in a vertex group (Be aware that vertex group store a float but only between 0 and 1 so you need to store the min max of the data). Note also that you need to have the piece of data for each vertex.

vg = ob.vertex_groups.get("data")
mini = min(data);
maxi = max(data);
for i,d  in enumerate(data):
    v = (q-mini+1e-20)/(maxi-mini+1e-20)
    vg.add([i], v, 'REPLACE');

Then you need to add two modifiers a skin modifier and a subdivision surface and you will end up with a tube. Note that you can control the radius of the tube by changing the mean radius X, Y.

Then you need to convert the vertex group to a vertex color layer :

vcol = ob.vertex_colors.new();
vcol.name = "data";
for poly in ob.data.polygons:
    for loop in poly.loop_indices:
        vertindex=ob.data.loops[loop].vertex_index;        
        weight=vgroup.weight(vertindex);
        vcol.data[loop].color = (weight, 0, 0);

Now you can access the vertex color using a attribute node (and an separate rbg node).

$\endgroup$
  • 2
    $\begingroup$ The question stipulates without converting to mesh, which appears to be the first step in your answer. $\endgroup$ – batFINGER Aug 15 '17 at 5:00
  • $\begingroup$ yes you are right..sorry my bad... I supposed that you want to bevel the curve with an object and that's why you don't want to loose the benefit of the curve.... $\endgroup$ – Stéphane Monté Aug 15 '17 at 9:38

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.