HSB Color Picker

1.7k views Asked by At

For a school assignment, I need to do something like this: HSB The 500x500 rectangle should show the different hue, saturation, and brightness of whatever color the mouse is pointing at, kind of like a Color Picker.

But since I suck at coding so much, I don't know what to do. I don't understand HSB very much. Here is my code and a picture of what I have right now. SS

void setup() {
  size(500,550);
}

void draw() {
 noStroke();
 colorMode(HSB, 100);
 for (int i = 0; i < 500; i++) {
  for (int j = 0; j < 50; j++) {
   int h = i-200;
   int s = j+500;
   int b = 500 + j;
    stroke(h,s,b);
    point(i, j);
  }
}
noStroke();
fill(mouseX, mouseY);
rect(0,50,500,500);
}

Any help would be appreciated. Thank you very much!

1

There are 1 answers

2
George Profenza On BEST ANSWER

You're already doing great using colorMode(HSB) to render the rainbow. I'd move nested for loop to setup to make it more efficient: it would rendered once and stay there since you're not calling background() and rect(0,50,500,500); is bellow the rainbow gradient.

HSB is actually easier to use than RGB. Here's a diagram from Wikipedia: HSV Color Clynder SharkD, CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0, via Wikimedia Commons

Value is the same a Brightness. Hue is typically in a 0-360 degrees range so picture going around in a rainbow circle, starting at red and ending at red. Let's say you start at red, at 0 degrees hue and you know yellow is at 60 degrees hue. Intuitively you'd find orange halfway between red and yellow at 30 degrees. In fact, if you go every 60 degrees you'll go through red, yellow, green, cyan, blue, magenta and back to red at 360/0.

Saturation and brightnesses typically go between 0-100%. Notice in the diagram above saturation increases away from the centre: 0 saturation = gray, 100% saturation = full tint.

Brightess increases from bottom to top in the diagram.

It's a bit strange you're mapping to 0-100 range for hue, saturation and brightness. Perhaps the intention was to simplify things by treating the hue as a percentage too.

One thing that could make things easier is the map() function. It maps a number from one range to another.

For example this bit of code is trying to remap i, j positions to hue and saturation.

  • i, j are in in 0-500, 0-50 range (x, y positions)
  • the top image show a rainbow gradient with the same saturation and brightness, so those could be kept constant:
  • simply map i from 0-499 range to 0-100 to map into hue

For example:

void setup() {
  size(500, 550);
  
  colorMode(HSB, 100);
  for (int i = 0; i < 500; i++) {
    for (int j = 0; j < 50; j++) {
      // remap i (x -> width) to 0 - 100 range for hue
      // since map returns a float, round helps make that an int
      int h = round(map(i, 0, 499, 0, 100));
      int s = 100;
      int b = 100; 
      stroke(h, s, b);
      point(i, j);
    }
  }
  
  noStroke();
  
}

void draw() {
  fill(map(mouseX, 0, width, 0, 100), map(mouseY, 0, height, 0, 100), 100);
  rect(0, 50, 500, 500);
}

In this particular case the 0-500 to 0-50 range is trivial: 500 / 100 = 5, therefore:

int h = i / 5;

will get you the same result as int h = round(map(i, 0, 499, 0, 100));, just don't have to think of the arithmetic too much.

In the nested for loop you're setting HSB colours. For the next part you'll need to get / read HSB colours and luckily Processing already provides hue(), saturation() and brightness() for you. For the color picker you'll only need hue().

To get the color under the cursor location on you simply call get(x, y) which returns a color a those coordinates.

If you look at the gradient image you notice:

  • the left side is fully saturated, the right side is desaturated (gray): x axis must map saturation
  • the top bit is light while the bottom is dark: y axis must map brightness

If you read the hue() when the mouse is clicked on the top rainbow gradient you can then make the larger gradient bellow by simply mapping x,y coordinates to saturation and brightness:

float hue;

void setup() {
  size(500, 550);
  
  colorMode(HSB, 100);
  for (int i = 0; i < 500; i++) {
    for (int j = 0; j < 50; j++) {
      // remap i (x -> width) to 0 - 100 range for hue
      // since map returns a float, round helps make that an int
      int h = round(map(i, 0, 499, 0, 100));
      int s = 100;
      int b = 100; 
      stroke(h, s, b);
      point(i, j);
    }
  }
  
  noStroke();
  
}

void draw() {
  // pick colour (hue)
  if(mousePressed){
    // check if the mouse was pressed on the top side only
    if((mouseX >= 0 && mouseX <= 500) &&
       (mouseY >= 0 && mouseY <= 50)){
      hue = hue(get(mouseX, mouseY));  
    }
  }
  // render saturation , brightness mapping
  for (int i = 0; i < 500; i++) {
    for (int j = 50; j < 550; j++) {
      int saturation = round(map(i, 0, 500, 100, 0));
      // swap output mapping range: brightness goes up when y decreases
      int brightness = round(map(j, 50, 550, 100, 0));
      stroke(hue, saturation, brightness);
      point(i, j);
    }
  }
}

You'll notice this runs a bit slow. It can be much faster using the pixels[]. However there are a few curve balls:

  1. you need to call loadPixels() before you can read pixels
  2. you need to convert x,y positions to an 1D array index: index = x + y * width
  3. you need to call updatePixels() after setting values to pixels[] to update

This will run much faster:

float hue;

void setup() {
  size(500, 550);
  
  colorMode(HSB, 100);
  for (int i = 0; i < 500; i++) {
    for (int j = 0; j < 50; j++) {
      // remap i (x -> width) to 0 - 100 range for hue
      // since map returns a float, round helps make that an int
      int h = round(map(i, 0, 499, 0, 100));
      int s = 100;
      int b = 100; 
      stroke(h, s, b);
      point(i, j);
    }
  }
  
  noStroke();
  
}

void draw() {
  // make latest pixels[] data available 
  loadPixels();
  // pick colour (hue)
  if(mousePressed){
    // check if the mouse was pressed on the top side only
    if((mouseX >= 0 && mouseX <= 500) &&
       (mouseY >= 0 && mouseY <= 50)){
      //hue = hue(get(mouseX, mouseY));
      hue = hue(pixels[mouseX + mouseY * width]);
    }
  }
  // render saturation , brightness mapping
  for (int i = 0; i < 500; i++) {
    for (int j = 50; j < 550; j++) {
      int saturation = round(map(i, 0, 500, 100, 0));
      // swap output mapping range: brightness goes up when y decreases
      int brightness = round(map(j, 50, 550, 100, 0));
      //stroke(hue, saturation, brightness);
      //point(i, j);
      pixels[i + j * width] = color(hue, saturation, brightness);
    }
  }
  // update
  updatePixels();
}