Dan Halliday
Dan Halliday

Reputation: 765

Calculate colours for points along an arbitrary gradient

I'm trying to write an 'apply gradient' function, which takes a bunch of points and a linear gradient and calculates the colour for each point. I have something like this (given an array of points and a gradient defined by two points start and end):

struct Colour {
    float red; float green; float blue;
};

struct Point {
    int x; int y;
    Colour colour;
};

Point points[total_points] = { ... };    
Point start = { ... };
Point end = { ... };

for (int i=0; i<total_points; i++) {

    // Get normalised position along x and y

    float scale_x = end.x-start.x;
    float pos_x = (scale_x == 0) ? 0 : (points[i].x-start.x) / scale_x;

    float scale_y = end.y-start.y;
    float pos_y = (scale_y == 0) ? 0 : (points[i].y-start.y) / scale_y;

    // Average the positions        
    float pos = .5 * (pos_x + pos_y);

    // Blend colours
    points[i].colour = blend_colour(start.colour, end.colour, pos);

}

My colour blending function is something simple like this:

static Colour blend_colour(Colour start, Colour end, float position) {

    Colour blend;

    blend.red = (start.red * (1-position)) + (end.red * position);
    blend.green = (start.green * (1-position)) + (end.green * position);
    blend.blue = (start.blue * (1-position)) + (end.blue * position);

    return blend;

}

My maths in the for loop is definitely not quite right — do I need to calculate the colour using trigonometry?

Upvotes: 0

Views: 160

Answers (1)

Jim Balter
Jim Balter

Reputation: 16406

Instead of

// Average the positions        
float pos = .5 * (pos_x + pos_y);

Try

// Get scaled distance to point by calculating hypotenuse
float dist = sqrt(pos_x*pos_x + pos_y*pos_y);

Also, while the compiler will do it for you, you ought to hoist your scale factors out of the loop. In fact, it might be better to calculate the scale factor for the distance:

Point start = { ... };
Point end = { ... };

float xdelta = end.x - start.x;
float ydelta = end.y - start.y;
float hypot = sqrt(xdelta*xdelta + ydelta*ydelta);

for (int i=0; i<total_points; i++) {

    // Get normalised distance to points[i]

    xdelta = points[i].x - start.x;
    ydelta = points[i].y - start.y;
    float dist = sqrt(xdelta*xdelta + ydelta*ydelta);
    if (hypot) dist /= hypot;

    // Blend colours 
    points[i].colour = blend_colour(start.colour, end.colour, dist);
}

Upvotes: 2

Related Questions