How do I lerp() colors on a curve? (non linear interpolation)

I find the idea great to get the amt data for lerpColor from a function.

So I made a class where you can

  • play with a bezier curve (instead of a function) and
  • the bezier curve (its y-value) is fed into the color.

Core idea is in the function testlerpColor().

It’s a bit tricky.

But when somebody wants to know more, I can explain. I received help from the forum for this. I was mixing up curve and bezier. Bad. See reference.

This could be extended with bezierVertex() or curveVertex() I guess.

Chrisir

// Test for a new bezier Class that can do bezier and bezierpoint (2D) to set a color from bezierPoint y-value 

BezierClass mybezier; 

// ---------------------------------------------------------------------------------------------

void setup() {

  size(1500, 900);

  final int STANDARD_DISTANCE=100;

  mybezier = new  BezierClass (
    width/2-31, 33, // coordinates for the first point / anchor
    width/2-STANDARD_DISTANCE, height/2+STANDARD_DISTANCE, //  control point (cp1)
    width/2+STANDARD_DISTANCE, height/2+STANDARD_DISTANCE, //  control point 
    width/2+31, 33  // coordinates for the second point / anchor 
    );
}

void draw() {
  background(#24B41D);

  fill(0); 
  text("Bezier\n"
    +"Demo for Bezier y value to color\n"
    +"You can drag the two red control points (and the anchor points) with the mouse. \n"
    +"Move the mouse to move the white ball and the color on the bezier (mouseX->amt)", 
    14, 14);

  testbezier();
}

//-------------------------------------------------------------------------------------------------------

void testbezier () {
  mybezier.displaybezier();    // show entire bezier  
  mybezier.testbezierData();   // show circles with texts
  mybezier.moveOnbezierWithMouseX(); // OR moveOnbezier()
  mybezier.testlerpColor();   // lerpColor
  mybezier.drag();            // for dragging with the mouse
}

//-------------------------------------------------------------------------------------------------------

void mousePressed() {
  mybezier.mousePressedClass();
}

void mouseReleased() {
  mybezier.mouseReleasedClass();
}

// ===============================================================================================

class BezierClass {

  final int UNKNOWN = -1; 

  // bezier data 
  PVector anchor1, cp1, cp2, anchor2;

  // the amt for lerp 
  float amt=0;

  // indicates which point we drag (cp1, cp2 or anchor1 or anchor2)
  int drag = -1; 

  // the min and max y for the current bezier
  float minY, maxY; 

  // how strong the cp influences !!! Doesn't work good........
  float factorCP = 1; 

  // constr - Parameters: you call the constr as you would bezier command (2D version) - see reference
  BezierClass ( float f0, float f1, 
    float f2, float f3, 
    float f4, float f5, 
    float f6, float f7 ) {
    // constr 
    anchor1 = new PVector (f0, f1);  
    cp1     = new PVector (f2, f3);
    cp2     = new PVector (f4, f5);
    anchor2 = new PVector (f6, f7);

    setMinYAndMaxY();
  } // constr 

  // ---

  void setMinYAndMaxY () {
    // init 
    minY = 1000000; 
    maxY = -100000;
    // simulate bezier 
    for (int i = 0; i <= 100; i++) {
      float y = bezierPoint(anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, 
        i/100.0);
      if (y<minY) 
        minY = y; 
      if (y>maxY) 
        maxY = y;
    }//for
  }

  // ---

  void displaybezier() {
    stroke(255, 0, 0); //RED 
    strokeWeight(1);
    noFill();
    bezier (
      anchor1.x, anchor1.y, 
      factorCP * cp1.x, factorCP * cp1.y, 
      factorCP * cp2.x, factorCP * cp2.y, 
      anchor2.x, anchor2.y
      );
  }

  // ---

  void moveOnbezier() {
    // AUTO move 
    // resulting point 
    float x, y; 
    x = bezierPoint(anchor1.x, factorCP * cp1.x, factorCP * cp2.x, anchor2.x, amt); 
    y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);
    fill(255); // WHITE
    noStroke(); 
    ellipse(x, y, 5, 5);
    amt+=0.01;
    if (amt>=1) 
      amt=0.0;
  }

  void moveOnbezierWithMouseX() {
    // mouseX move
    float x, y; 
    // mouseX -> amt :
    amt=map(mouseX, 0, width, 
      0, 1); 
    // amt ->bezier point
    x = bezierPoint(anchor1.x, factorCP * cp1.x, factorCP * cp2.x, anchor2.x, amt); 
    y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);

    //x = bezierPoint(factorCP * cp1.x, anchor1.x, anchor2.x, factorCP * cp2.x, amt); 
    //y = bezierPoint(factorCP * cp1.y, anchor1.y, anchor2.y, factorCP * cp2.y, amt);
    fill(255); // WHITE
    noStroke(); 
    ellipse(x, y, 5, 5);
  }

  // ---

  void testlerpColor() {
    // now it gets interesting 

    // mouseX to lerpColor 
    // mouseX -> amt : 
    float amt=map(mouseX, 0, width, 
      0, 1); 
    // amt -> bezierPoint Y 
    float y = bezierPoint( anchor1.y, factorCP * cp1.y, factorCP * cp2.y, anchor2.y, amt);  
    // y-value -> lerp color colorAmt
    float colorAmt = map ( y, minY, maxY, 
      0, 1  );
    // lerp color colorAmt -> color 
    fill( lerpColor(  color(255, 0, 0), color(0, 0, 255), colorAmt ) ); 
    rect (100, 100, 66, 66);

    fill(255);
    text ("min and max: " + minY+"  "+maxY 
      + " -> " +y 
      + " \ncolor amt = "+colorAmt, 100, 180);
  }

  // ---

  void testbezierData() {
    makeCircleWithText("cp1", cp1, color (255, 0, 0)) ;  
    makeCircleWithText("cp2", cp2, color (255, 0, 0)) ;  
    makeCircleWithText("anchor1", anchor1, color (0, 0, 255)) ;  
    makeCircleWithText("anchor2", anchor2, color (0, 0, 255)) ;
  }//method

  void makeCircleWithText(String txt, PVector pv, color col) {
    noStroke(); 
    fill(col);
    ellipse (pv.x, pv.y, 18, 18);

    fill(255);
    text(txt, 
      pv.x+15, pv.y-8);
  }

  // ---

  void mousePressedClass() {
    if (dist(mouseX, mouseY, cp1.x, cp1.y) < 33) 
      drag=0; 
    else if (dist(mouseX, mouseY, cp2.x, cp2.y) < 33) 
      drag=1;
    else if (dist(mouseX, mouseY, anchor1.x, anchor1.y) < 33) 
      drag=2;
    else if (dist(mouseX, mouseY, anchor2.x, anchor2.y) < 33) 
      drag=3;
  }//method 

  void mouseReleasedClass() {
    drag=UNKNOWN; // reset
    setMinYAndMaxY();
  }

  // ---

  void drag() {
    if (drag==UNKNOWN) 
      return; 

    switch (drag) {
    case 0:
      cp1.x=mouseX; 
      cp1.y=mouseY;
      break; 

    case 1:
      cp2.x=mouseX; 
      cp2.y=mouseY;
      break;

    case 2:
      anchor1.x=mouseX; 
      anchor1.y=mouseY;
      break;

    case 3:
      anchor2.x=mouseX; 
      anchor2.y=mouseY;
      break;

    case UNKNOWN:
      //ignore
      break;

    default:
      // Error
      break;
    }//switch

    setMinYAndMaxY();
  }//method
  //
}//class
//