How to draw a complete color wheel?

edited December 2015 in Arduino

Hello people,

I'm trying to create a program that I can use to control the LED strips inside my PC using Arduino. I currently have communication and a lot of other parts working already, but I am stuck on a small problem.

I am able to draw a color wheel or rectangle in this case that should represent all colors available. The program looks at the value under the mouse while it is pressed, and sends this to the Arduino. The problem I am having is that the colors nearest to the edge are not picked up as pure red (255,0,0), green or blue. The closest value I can get is: (255,32,32).

What I have tried so far is changing the size of the rectangle to exclude the missing colors being rendered off-screen. I have found that the code reading the color value under the cursor is correct - I did this by drawing a plain pure red rectangle in the corner, which returned the correct value (255,0,0). I also found that simply subtracting 32 from the values is incorrect, as on pure white you will find the value to be (223,223,223).

This causes significant problems with my LEDs, as a value of 32 on the PWM pin will light the corresponding color up by quite a bit because of the non-linear relationship between LED volt and brightness.

So to reiterate my question: how do I draw a color circle or rectangle from which I can get 100% red, blue and green as well?

Here is my code:

import processing.serial.*; 

Serial myPort;    // The serial port
String inString;  // Input string from serial port
int lf = 10;      // ASCII linefeed 
int[] rgb = new int[3];
PImage m;

void setup(){
  size(400,400);
  frameRate(120);
  smooth(4);
  ellipseMode(CENTER);
  background(255);

  colorMode(HSB, 400);
  for (int i = 0; i < 400; i++) {
    for (int j = 0; j < 400; j++) {
      stroke(i, j, 400);
      point(i, j);
    }
  }
  m=get();

  myPort = new Serial(this, Serial.list()[1], 9600); 
  myPort.bufferUntil(lf);
}

void draw(){
  image(m,0,0);
  if(mousePressed){
    color c = get(mouseX,mouseY);
    fill(c);
    stroke(0);
    ellipse(mouseX,mouseY,30,30);
    rgb[0] = c >> 16 & 0xFF;
    rgb[1] = c >> 8 & 0xFF;
    rgb[2] = c & 0xFF;

    fill(0);
    text(rgb[0], 10,70);
    text(rgb[1], 10,90);
    text(rgb[2], 10,110);

    String test = ",";
    test += str(rgb[0]);
    test += "+";
    test += str(rgb[1]);
    test += "+";
    test += str(rgb[2]);
    test += "\n";

    myPort.write(test);
  }
  fill(0);
  text("Received: " + inString, 10,50);
}

void serialEvent(Serial p) { 
  inString = p.readString(); 
} 

Answers

  • edited December 2015

    Maybe you could do something like this? You can use atan2(y, x) to return the angle from the origin in radians (values range from -pi to pi). You can then map that from 0 to 255 and use it as the hue in HSB mode. If you want to get the RGB values while in HSB mode, you can use something like redValue = red(c). I've made it so that it prints out the values on the console, rather than a serial output.

    float hue;
    color c;
    float r;
    float g;
    float b;
    float xPos;
    float yPos;
    
    void setup() {
      size(800, 450);
    }
    
    void draw() {
      background(0);
      colorMode(HSB);
      xPos = mouseX - width/2;
      yPos = -(mouseY - height/2);
      hue = atan2(yPos, xPos); // atan2(y, x) lets you get the angle
      hue = map(hue, -PI, PI, 0, 255);
      c = color(hue, 255, 255);
      fill(c);
      noStroke();
      ellipseMode(CENTER);
      ellipse(mouseX, mouseY, 50, 50);
      textSize(30);
      text(hue, 25 + mouseX, mouseY);
      stroke(128);
      line(width/2, height/2, mouseX, mouseY);
    
      r = red(c);
      g = green(c);
      b = blue(c);
    
      print(r); print('\t'); print(g); print('\t'); println(b);
    }   
    
  • Answer ✓

    Smooth () could be introducing some anti aliasing which is changing the actual colours on screen - might explain the 255, 32, 32

    But CCC's solution should be more accurate.

  • @ChrisCrossCrash

    That looks like a potential solution by simply calculating what should be under the mouse and then converting those coordinates to RGB. This seems more robust than picking up on the actual color under the mouse by using the get() function, but more effort.

    @Koogs

    Aha! That sound plausible - I'll have to try this out when I get back from work. Part of why I would like to keep using the get() function is because what I see on the screen is what I get. It also would be easier to change the configuration of the color shape without having to update the code that converts the coordinates of the mouse to the corresponding RGB values.

    Cheers for the suggestions guys, I will get back if I can make it work.

  • I have found that the problem was indeed because of the smooth(); function. I have replaced it with noSmooth(); which draws the colors accurately now.

    Thank you very much!

Sign In or Register to comment.