shnitzelperson
shnitzelperson

Reputation: 61

How do I fill a rectangle or ellipse with a gradient in Processing?

I am trying to get my paddles from white to gradient (linear) and the ball to have a radial gradient. Thanks for your help!You can find the code for the paddles in void drawPaddle.

This is my goal: Desired final product

This is my code: //Ball int ballX = 500; int ballY = 350; int ballHeight = 35; int ballWidth = 35; int speedX = 4; int speedY = 4; int directionX = 1;
int directionY = 1;

//Paddles
int player1X = 30;
int player2X = 830;
int player1Y = 350;
int player2Y = 350;

//Healthbars
int bar1X = 100;
int bar1Y = 20;
int player1health = 100;
int bar1colour = #22E515;
int bar2X = 700;
int bar2Y = 20;
int player2health = 100;
int bar2colour = #22E515;

//Movements
boolean upX = false;
boolean downX = false;
boolean upY = false;
boolean downY = false;

void setup() {
  size(900, 700);
}

void draw() {
  background(55, 68, 120);

drawPaddle();

  //EmptySpace**
  fill(55, 68, 120);
  noStroke();
  rect(player1X, player1Y, 40, 140);
  rect(player2X, player2Y, 40, 140);
  
  //Healthbars
  fill(bar1colour);
  rect(bar1X, bar1Y, player1health, 15);
  fill(bar2colour);
  rect(bar2X, bar2Y, player2health, 15);

  //Ball
  fill(194, 16, 0);
  ellipse(ballX, ballY, ballHeight, ballWidth);
  

  moveCircle();
  movePaddle();
  moveCollisions();
}

void drawPaddle() {  
  fill(255);
  noStroke();
  rect(30, 0, 40, 1000);
  rect(830, 0, 40, 1000);
}

void moveCircle() {  
  ballX = ballX + speedX * 1;
  ballY = ballY + speedY * 1;

  if (ballX > width- ballWidth +20 || ballX < ballWidth) {
    speedX *= -1;
  }
  if (ballY > height- ballHeight +20 || ballY < ballHeight) {
    speedY *= -1;
  }
}

void movePaddle() {
  //key movements
  if (upX == true) {
    player1Y = player1Y - 5;
  }
  if (downX == true) {
    player1Y = player1Y + 5;
  }
  if (upY == true) {
    player2Y = player2Y - 5;
  }
  if (downY == true) {
    player2Y = player2Y + 5;
  } 

  //Wrap around
  if (player1Y > 700) {
    player1Y = 0;
  } else if (player1Y + 140 < 0) {
    player1Y = 700;
  }
  if (player2Y > 700) {
    player2Y = 0;
  } else if (player2Y + 140 < 0) {
    player2Y = 700;
  }
}

void moveCollisions() {
  //Collisions
  if ((ballX - ballWidth / 2 < player1X + 40) && ((ballY - ballHeight / 2 > player1Y + 140) || (ballY + ballHeight / 2 < player1Y))) {
    if (speedX < 0) {
      player1health -= 20;
      speedX = -speedX*1;
      if (player1health == 20) {
        bar1colour = #F51911;
      }
    }
  } else if ((ballX + ballWidth / 2 > player2X) && ((ballY - ballHeight / 2 > player2Y + 140) || (ballY + ballHeight/2 < player2Y))) {
    if (speedX > 0) {
      player2health -= 20;
      bar2X += 20;
      speedX = -speedX*1;
      if (player2health == 20) {
        bar2colour = #F51911;
      }
    }
  }
}

void keyPressed() {
  if (key == 'w' || key == 'W') {
    upX = true;
  } else if (key == 's' || key == 'S') {
    downX = true;
  } else if (keyCode == UP) {
    upY = true;
  } else if (keyCode == DOWN) {
    downY = true;
  }
}

void keyReleased() {
  if (key == 'w' || key == 'W') {
    upX = false;
  } else if (key == 's' || key == 'S') {
    downX = false;
  } else if (keyCode == UP) {
    upY = false;
  } else if (keyCode == DOWN) {
    downY = false;
  }
}

Upvotes: 5

Views: 3709

Answers (3)

George Profenza
George Profenza

Reputation: 51857

Nice question and great mycycle and void main's answers are great already. The PeasyGradients library looks great!

I would like to contribute a minor workaround: using the P2D renderer to handle the gradient via shapes:

size(100,100,P2D);// render via OpenGL
noStroke();
// draw the rect by manually setting vertices
beginShape();
// gradient start
fill(#fef1a6);
vertex(0  ,  0); // TL  
vertex(100,  0); // TR
// gradient end
fill(#e28ab9);
vertex(100,100); // BR
vertex(  0,100); // BL

endShape();

linear gradient P2D

For more complex shapes this could be used be used in conjuction with mask(). Not only PImage can be masked, but also PGraphics, since they inherit from PImage:

PGraphics gradient;
PGraphics mask;

void setup() {
  size(640, 360, P2D);
  
  gradient = getGradientRect(width, height, #fef1a6, #e28ab9);
  mask = getMaskRect(width, height);
}

void draw() {
  background(#483b6c);
  drawPongShapes();
  // apply pong shapes mask to gradient
  gradient.mask(mask);
  // render masked gradient
  image(gradient, 0, 0);
}
// draw white shapes (masks) on black background
void drawPongShapes(){
  mask.beginDraw();
  mask.background(0);
  mask.ellipse(width * 0.5, height * 0.5, 60, 60);
  mask.rect(width * .25, mouseY, 30, 150);
  mask.rect(width * .75, height-mouseY, 30, 150);
  mask.endDraw();
}

PGraphics getMaskRect(int w, int h) {
  PGraphics layer = createGraphics(w, h, P2D);
  layer.beginDraw();
  layer.background(0);
  layer.noStroke();
  layer.fill(255);
  layer.ellipseMode(CENTER);
  layer.rectMode(CENTER);
  layer.endDraw();
  return layer;
}

PGraphics getGradientRect(int w, int h, color grad1, color grad2) {
  PGraphics layer = createGraphics(w, h, P2D);
  layer.beginDraw();
  layer.noStroke();
  // draw rect as shape quad
  layer.beginShape();
  
  // gradient start
  layer.fill(grad1);
  layer.vertex(0, 0); // TL  
  layer.vertex(w, 0); // TR
  // gradient end
  layer.fill(grad2);
  layer.vertex(w, h); // BR
  layer.vertex(0, h); // BL
  
  layer.endShape();
  layer.endDraw();
  return layer;
}

pong-linear-gradient

Upvotes: 1

micycle
micycle

Reputation: 3820

I wrote a library just for this kind of purpose (drawing colour gradients in Processing) called PeasyGradients — download the .jar from the Github releases and drag-and-drop it onto your sketch. It renders 1D gradients as 2D spectrums into your sketch or a given PGraphics object.

Here's an example of drawing linear and radial spectrums using your desired gradient:

PeasyGradients renderer;
PGraphics rectangle, circle, circleMask;

final Gradient pinkToYellow = new Gradient(color(227, 140, 185), color(255, 241, 166));

void setup() {
  size(800, 800);

  renderer = new PeasyGradients(this);

  rectangle = createGraphics(100, 400);
  renderer.setRenderTarget(rectangle); // render into rectangle PGraphics
  renderer.linearGradient(pinkToYellow, PI/2); // gradient, angle

  circle = createGraphics(400, 400);
  renderer.setRenderTarget(circle);  // render into circle PGraphics
  renderer.radialGradient(pinkToYellow, new PVector(200, 200), 0.5); // gradient, midpoint, zoom

  // circle is currently a square image of a radial gradient, so needs masking to be circular  
  circleMask = createGraphics(400, 400);
  circleMask.beginDraw();
  circleMask.fill(255); // keep white pixels
  circleMask.circle(200, 200, 400);
  circleMask.endDraw();

  circle.mask(circleMask);
}

void draw() {
  background(255);

  image(rectangle, 50, 50);

  image(circle, 250, 250);
}

enter image description here

Upvotes: 2

void main
void main

Reputation: 125

What you can try is make a PGraphics object for each rectangle you are drawing, fill it with gradient color using linear interpolation and then instead of using rect(x1, y1, x2, y2), in drawPaddle() use image(pgraphic, x, y).

Here is how you can create a gradient effect using lerpColor() in processing:

  • Make a start point say (x1, y1) and end point say (x2, y2) of the gradient.
  • Make a starting and a ending color say c1 and c2.
  • Now for a point P (x, y), calculate the distance between start point and P and divide it by the distance between the start point and and end point. This will be the 'amount' to lerp from start color and end color. float t = dist(x1, y1, x, y) / dist(x1, x2, y1, y2)
  • Put this value in lerpColor() as lerpColor(c1, c2, value). This will give you the color for point P.
  • Repeat the same for every point you want to calculate the gradient for.

Here's an example: Note: here, i am taking t which is the amount to be lerped as the distance between the starting point of gradient divided by the distance between the start and end point of gradient, as it will always be a value in between 0 and 1.

PGraphics g = createGraphics(50, 200); // set these values to the size(width, height) of paddle you want 
    color startColor = color(255, 25, 25); // color of start of the gradient
    color endColor   = color(25, 25, 255); // color of end of the gradient
    
    g.beginDraw(); //start drawing in this as you would draw in the screen
    g.loadPixels();//load pixels, as we are going to set the color of this, pixel-by-pixel
    
    int x1 = g.width/2;
    int y1 = 0;
    int x2 = g.width/2;
    int y2 = g.height;
    
    // (x1, y1) is the start point of gradient
    // (x2, y2) is the end point of gradient
    
    // loop through all the pixels
    for (int x=0; x<g.width; x++) {
        for (int y=0; y<g.height; y++) {
            
            //amout to lerp, the closer this is to (x2, y2) it will get closer to endColor
            float t = dist(x1, y1, x, y) / dist(x1, y1, x2, y2);
            
            // you need to convert 2D indices to 1D, as pixels[] is an 1D array
            int index = x + y * g.width;
            g.pixels[index] = lerpColor(startColor, endColor, t);
        }
    }
    g.updatePixels(); // update the pixels
    g.endDraw(); // we are done drawing into this
    
    // Now, you can draw this using image(g, x, y);

Here is the result when i created this in the global scope and then drew it in draw() using image(g, width/2 ,height/2):

result

Now, you can modify everything to your preference.
Please mark this as answer if it helped you in any way.

Upvotes: 1

Related Questions