how to draw a gradient-colored line?

edited June 2014 in How To...

Subject says it all. I'd like to draw a line that gradually changes color from one end to the other.

I'm thinking I should avoid points/pixels because I want the line to look smooth and when I zoom in on a line that's been drawn with smooth(), it looks complicated to replicate. I made a rough pass at drawing shorter and shorter lines (first line is full length at one color, then as they get shorter I lerpColor() the colors), .... but they didn't come out all that smooth.

Thanks for any suggestions-

Tagged:

Answers

  • edited June 2014

    I just tried out placing some number of line segments using the parametric line equation but unfortunately it causes the overall line segment to look a bit wiggly sometimes. From a math point of view, the values should be correct, but I'm guessing that some kind of rounding does not agree with Processing's built-in lines:

    // Change this to set the number of line segments
    int numSegments = 10;
    
    int colorStep = 255/numSegments;
    float tStep = 1.0/numSegments;
    float x1, y1, x2, y2;
    
    void setup() {
      size(400, 400);
      strokeWeight(3);
      reset();
    }
    
    void draw() {
      background(255);
    
      // Replace this with your own color logic
      int currColor = 0;
    
      // float t should have values between 0.0 and 1.0
      // When t = 0.0 it represents a point at (x1, y1)
      // When t = 1.0 it represents a point at (x2, y2)
      // t = 0.25 is a quarter of the way from (x1, y1) in the direction of (x2, y2)
      float t = 0.0;
    
      // These multipled by t plus (x1, y1) move in the direction of (x2, y2)
      float deltaX = x2-x1;
      float deltaY = y2-y1;
    
      for (int i = 0; i < numSegments; i++) {
        // Replace this with your own color logic
        stroke(currColor, 0, 0);
        currColor += colorStep;
    
        // Draw a line segment
        float nextT = t+tStep;
        line(x1+deltaX*t, y1+deltaY*t, x1+deltaX*nextT, y1+deltaY*nextT);
        t = nextT;
      }
    }
    
    void mousePressed() {
      reset();
    }
    
    void reset() {
      x1 = random(width);
      y1 = random(height);
      x2 = random(width);
      y2 = random(height);
    }
    

    Edit: The rounding problem may be avoidable by using your own line function instead of Processing's built-in one.

  • I think lerpColor() is a very useful method for this. Another good solution may be to use the P2D renderer and per-vertex colors of a beginShape-endShape. Then OpenGL will take care of the color interpolation. Of course lerpColor() could be used then as well to determine the colors for each vertex.

    Here is an example using lerpColor on line segments:

    void setup() {
      size(1280, 720);
      strokeWeight(10);
      smooth();
    }
    
    void draw() {
      background(255);
      line(mouseX, mouseY, int(noise(3 + frameCount * 0.003) * width), int(noise(5 + frameCount * 0.01) * height), color(255, 0, 0), color(0, 255, 0), 60);
      line(mouseX, mouseY, int(noise(13 + frameCount * 0.002) * width), int(noise(15 + frameCount * 0.01) * height), color(0, 0, 255), color(255, 255, 0), 60);
      line(mouseX, mouseY, int(noise(6 + frameCount * 0.005) * width), int(noise(8 + frameCount * 0.01) * height), color(0, 255, 255), color(0, 255, 0), 60);
      line(mouseX, mouseY, int(noise(8 + frameCount * 0.005) * width), int(noise(18 + frameCount * 0.01) * height), color(255, 0, 255), color(125, 255, 125), 60);
      line(mouseX, mouseY, int(noise(4 + frameCount * 0.005) * width), int(noise(11 + frameCount * 0.01) * height), color(0, 0, 255), color(225, 205, 25), 60);
    }
    
    void line(int x_s, int y_s, int x_e, int y_e, int col_s, int col_e, int steps) {
      float[] xs = new float[steps];
      float[] ys = new float[steps];
      color[] cs = new color[steps];
      for (int i=0; i<steps; i++) {
        float amt = (float) i / steps;
        xs[i] = lerp(x_s, x_e, amt) + amt * (noise(frameCount * 0.01 + amt) * 200 - 100);
        ys[i] = lerp(y_s, y_e, amt) + amt * (noise(2 + frameCount * 0.01 + amt) * 200 - 100);
        cs[i] = lerpColor(col_s, col_e, amt);
      }
      for (int i=0; i<steps-1; i++) {
        stroke(cs[i]);
        line(xs[i], ys[i], xs[i+1], ys[i+1]);
      }
    }
    
  • I used a bunch of circles with 1 pixel spacing between each! No openGL graphics card is required. I have had no issues with speed on my processor(an Intel Centrino). use drawRainbow() like line, except that the fifth parameter specifies the speed of the rainbow, 64 being one repeat and higher// use rainbowWeight() like strokeWeight(). code is as follows:

     void drawRainbow(int p2x, int p2y, int p1x, int p1y, float range)
      {
        for(float iteration = 0; iteration < 1; iteration+=0.001)
        {
        float x = p1x + (iteration * (p2x - p1x));
        float y = p1y + (iteration * (p2y - p1y));
        ellipse(x, y, c_wtn2, c_wtn2);
        hue = hue + range / 255;
        fill(hue%255, 255, 255);
        }
      }
      void rainbowWeight(int weight)
      {
        c_wtn2 = weight;
      }
    
  • @processingRGB, that actually does not place one circle per pixel, it places 1000 circles no matter what. Try drawing a line that is 10000 units across and you'll see gaps.

    The resulting line is very smooth though, I tried making another sketch that uses code from the ones posted here:

    float x1, y1, x2, y2;
    color a, b;
    
    void setup() {
      size(600, 600);
      noStroke();
      a = color(255, 0, 0);
      b = color(0, 255, 0);
      reset();
    }
    
    void draw() {
      background(0);
      gradientLine(x1, x2, y1, y2, a, b);
    }
    
    void mousePressed() {
      reset();
    }
    
    void gradientLine(float x1, float y1, float x2, float y2, color a, color b) {
      float deltaX = x2-x1;
      float deltaY = y2-y1;
      float tStep = 1.0/dist(x1, y1, x2, y2);
      for (float t = 0.0; t < 1.0; t += tStep) {
        fill(lerpColor(a, b, t));
        ellipse(x1+t*deltaX,  y1+t*deltaY, 3, 3);
      }
    }
    
    void reset() {
      x1 = random(width);
      y1 = random(height);
      x2 = random(width);
      y2 = random(height);
    }
    
  • Thanks for this! The code that @asimes posted works perfectly.

Sign In or Register to comment.