A 1-D transformation to RGB Colors

The task is to find a way to represent RGB colors from using a single variable $x$. I could use HSV where $h=x$ and set $s$ and $v$ to a constant, that would work, but in HSV $h$ is an angle that goes from 0 to 360, and so small $h$ ($h=\delta$) and and large $h$) ($h=360-\delta$) will give the same RGB value, which is not what you might want.

The key to making this transformation is that it be two way: $x\to RGB$ and $RBG\to x$, and the $RGB\to x$ is the hard part because we want things to be single valued. Since we have 3 colors (RGB), we want a transformation $x\to RGB$ that always has at least one of the 3 colors off. And, we want the colors to wax and wane, in a continuous way. Something like the following seems to work. You can drag around the inside of the black box and it will show you the transformation from $x$, the horizontal scale, to the 3 colors $R$, $G$, $B$. Note that we've defined 6 regions, where the conditions in each region are constant ($R$, $G$, $B$ don't change or change linearly): $I\to x=[0,0.2)$, $II\to x=[0.2,0.3)$, $III\to x=[0.3,0.5)$, $IV\to x=[0.5,0.6)$, $V\to x=[0.6,0.8)$, $VI\to x=[0.8,1.0]$.

   
R: G: B:
Color:
The code to do this looks like this:
function xtorgb(x) {
  //
  // x goes between 0 and 1
  //
  let r=0, g=0, b=0;
  if (x < 0.2) {
    r = Math.floor( 255*x/0.3 );
    g = 0;
    b = 0;
  }
  else if (x < 0.3) {
    r = Math.floor( 255*x/0.3 );
    g = Math.floor( 255*(x-0.2)/0.3 );
    b = 0;
  }
  else if (x < 0.4) {
    r = Math.floor( 255*(1-(x-0.3)/0.3) );
    g = Math.floor( 255*(x-0.2)/0.3 );
    b = 0;
  }
  else if (x < 0.5) {
    r = Math.floor( 255*(1-(x-0.3)/0.3) );
    g = Math.floor( 255*(x-0.2)/0.3 );
    b = 0;
  }
  else if (x < 0.6) {
    r = Math.floor( 255*(1-(x-0.3)/0.3) );
    g = Math.floor( 255*(1-(x-0.5)/0.3) );
    b = 0;
  }
  else if (x < 0.7) {
    r = 0;
    g = Math.floor( 255*(1-(x-0.5)/0.3) );
    b = Math.floor( 255*(x-0.6)/0.4 );
  }
  else if (x < 0.8) {
    r = 0;
    g = Math.floor( 255*(1-(x-0.5)/0.3) );
    b = Math.floor( 255*(x-0.6)/0.4 );
  }
  else {
    r = 0;
    g = 0;
    b = Math.floor( 255*(x-0.6)/0.4 );
  }
  return {r:r, g:g, b:b}
}
where $x$ is the input and the return is an object, with members $r$, $g$, $b$, like this:
  nc = xtorgb(x);
  r = nc.r;
  g = nc.g;
  b = nc.b;
To turn $(r,g,b)$ into a color of the form "#$ABCDEF$", you can use:
  color = intstocolor(r,g,b);
where the code for that is here:
function intstocolor(r1,r2,r3) {
    s1 = r1.toString(16).toUpperCase();
    if (r1 < 16) s1 = "0"+s1;
    s2 = r2.toString(16).toUpperCase();
    if (r2 < 16) s2 = "0"+s2;
    s3 = r3.toString(16).toUpperCase();
    if (r3 < 16) s3 = "0"+s3;
    let newc = "#"+s1+s2+s3;
    return newc;    
}

Now for the reverse transformation. This is a lot easier to code up than you might think. It's just a series of questions asking which of the 3 colors is identically 00, and branching. The flow chart looks like this:

Starting from the top, $G=0?$, this tells us that we are either in region $I$ or region $V-VI$. In region $I$, $R>0$ so if we test on $R=0?$ we will know if we are to the left or right of where $G>0$. If $R=0$, then we are to the right, and the value for $x$ is determined entirely by the value of $B$. If we are to the left, then we use the value of $R$ to determine $x$. If we are in the region where $G>0$, then if $R=0$ we are in region $V$ and again we can use the value for $B$ to determine $x$. If $R>0$ then we are either in region $II$-$III$ where $G$ is increasing, or region $IV$ where $G$ is decreasing, but either way all we need to know if the value for $G$ to determine $x$. Voila. The canvas below shows the color scheme as $x$ increases bottom to top. Click on a color and it will show you the corresponding value of $x$.

The code to implement this is here:
function rgbtox(r,g,b) {
  let x = -1;
  if (g == 0) {
    if (r == 0) x = (0.4*b/255) + 0.6;
    else x = .3*r/255;
  }
  else {
    if (r == 0) x = (0.4*b/255) + 0.6;
    else {
      if (r < 255/3) x = (0.3*(1-g/255)) + 0.5;
      else x = (0.3*g/255) + 0.2;
    }
  }
  return x;
}
Note: the color scheme here increases from red ("coolest") to blue ("hottest"). If you want to change it so that it goes the other way, just interchange $r$ and $b$ in the two routines above (xtorgb and rgbtox).
Drew Baden. Aug 15, 2020

All rights reserved. No part of this publication may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the publisher, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law.