1

Topic: RGB color control to HSV

I would really like to have an option for the RGB color control to report Hue Saturation and Value (HSV), rather than red, green and blue.   This would more naturally reflect what the interface is allowing the user to select and allow the programmer to pick his or her own HSV to RGB conversion algorithms (such as those provided by the FastLED library).

In the meantime, could you please publish the algorithm you use to convert RGB values to the position of the sliders on the RGB color control element so I don't have to reverse-engineer it?

Chad

2

Re: RGB color control to HSV

If anyone is interested, I wrote a C function that converts rgb values reported by RemoteXY to CHSV colors for FastLED using their "Rainbow" definition of Hue.   Enjoy.

#include "fastled.h"

/// RemoteXYrgb2hsv converts an RGB color reported by the RemoteXY.com RGB
//  color input and converts it into a FastLED CHSV color.
//
// The *hue* returned will use the rainbow spectrum defined by FastLED here:
//   https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
//
// The *saturation* and *value* will represent the position of the inner wheel
// of the RemoteXY color input control.
// - saturation is 0 on the top half of the wheel (i.e. no hue)
// - saturation goes from 0 to 255 on the bottom half of the wheel.
// - value goes from 0 on the left to 255 on the right.
//
// Note:  While this does give a useful color for use with the FastLED
// library, converting to CHSV and back to CRGB will not result in the same
// RGB values reported by RemoteXY.  For example, there is no CHSV color that
// converts to CRGB::Yellow. CRGB::Yellow maxes out red and green to 255, but
// CHSV yellow has equal amounts of red and green, but less than 255.
//
CHSV RemoteXYrgb2hsv(const CRGB &rgb)
{
  CHSV hsv;
  uint8_t max_color = max(max(rgb.r, rgb.g), rgb.b);
  uint8_t min_color = min(min(rgb.r, rgb.g), rgb.b);

  // RemoteXY "value" is the brightest color component.
  hsv.v = max_color;

  if (max_color == min_color) {
    // r,g,b are all the same which means we have a shade of gray with no
    // saturation at all. hue is meaningless in this case, so we arbitrarily
    // set it to 0
    hsv.s = 0;
    hsv.h = 0;
  } else {
    // First, compute the saturation.  If only one or two primary colors are
    // mixed, then saturation is maxed out at 255.  But if a third color
    // component is included (the min_color), it lowers color saturation,
    // approaching zero as the min_color appoaches the max_color
    hsv.s = map(min_color, 0, max_color, 255, 0);

    // Next we calculate the hue. 
    // This is complicated because the RGB colors are not evenly spaced
    // mathematically in the FastLED "rainbow" spectrum:
    //   https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
    //
    // First, we find the max color component (red, green or blue).  Our hue
    // will somewhere in between the hue of the max color component and half
    // way to the second most intense color component. For example, if the
    // primary color component is green then our hue will either be between
    // green and yellow if the secondary color is red, or between green and
    // aqua if the secondary color is blue.  This hue is linearly
    // interpolated as the secondary color intensity from the third color
    // intensity to maximally differentiated.
    const int HUE_HIGH_RED=256;
    const int HUE_BLUE_RED=(HUE_BLUE+HUE_HIGH_RED)/2;
    if (max_color == rgb.r) {
      if (rgb.g > rgb.b) {
        hsv.h = map(rgb.g, min_color, max_color, HUE_RED, HUE_YELLOW);
      } else {
        hsv.h = map(rgb.b, min_color, max_color, HUE_HIGH_RED, HUE_BLUE_RED);
      }
    }
    else if (max_color == rgb.g) {
      if (rgb.b > rgb.r) {
        hsv.h = map(rgb.b, min_color, max_color, HUE_GREEN, HUE_AQUA);
      } else {
        hsv.h = map(rgb.r, min_color, max_color, HUE_GREEN, HUE_GREEN);
      }
    }
    else {  // max_color == b
      if (rgb.r > rgb.g) {
        hsv.h = map(rgb.r, min_color, max_color, HUE_BLUE, HUE_BLUE_RED);
      } else {
        hsv.h = map(rgb.g, min_color, max_color, HUE_BLUE, HUE_AQUA);
      }
    }
  }
  return hsv;
}