HSV to RGB

by Lamik

HTML

<div>
  <button class="btn" onclick="setColIndexChart(0)">Chart R</button>
  <button class="btn" onclick="setColIndexChart(1)">Chart G</button>
  <button class="btn" onclick="setColIndexChart(2)">Chart B</button>
</div>
<div>Value <input type="range" min="0" max="100" value="50" oninput="setVal(event)"></div>
<div>Saturation <input type="range" min="0" max="100" value="100" oninput="setSat(event)"></div>

<div>Wiki-Opt max <span id="error"></span> </div>

<div id="labelOpt">Optimized</div>
<svg viewBox="0 0 360 60" class="chart">    
  <text x="0" y="59" class="axis">0</text>
  <text x="0" y="10" class="axis">1</text>
  <text x="320" y="59" class="axis">360 hue</text>
  <polyline id="chartA" fill="none" stroke="#0074d9" stroke-width="1" points=""/>
</svg>

<div id="labelWiki">Wiki</div>
<svg viewBox="0 0 360 60" class="chart">    
  <text x="0" y="59" class="axis">0</text>
  <text x="0" y="10" class="axis">1</text>
  <text x="320" y="59" class="axis">360 hue</text>
  <polyline id="chartB" fill="none" stroke="#0074d9" stroke-width="1" points=""/> 
</svg>

<div class="test">  
  <div id="colA" class="col"></div>
  <div id="colB" class="col"></div>
</div>

CSS

.col { margin: 5px; width: 50px; height: 50px; }
#btn { display: block; margin: 10px; }
.test { display: flex; height: 400px}
.color { width: 50px; height: 1px; }
.chart { border: 1px solid black; }
.axis { font: 10px sans-serif;}
#labelOpt { margin-top: 20px }

JavaScript

function hsv2rgb(h,s,v) 
{                 
  let f= (n,k=(n+h/60)%6) => v-v*s*Math.max(Math.min(k,4-k,1),0);        
  return [f(5),f(3),f(1)];  
}

function hsv2rgb_wiki(h,s,v) {            
  let c =v*s; 
  let k = h/60;
  let x = c*(1 - Math.abs(k%2-1));
 
  let r1 = g1 = b1 = 0;
    
  if(k>=0 && k<=1) { r1=c; g1=x }
  if(k>1 && k<=2)  { r1=x; g1=c }
  if(k>2 && k<=3)  { g1=c; b1=x }
  if(k>3 && k<=4)  { g1=x; b1=c }
  if(k>4 && k<=5)  { r1=x; b1=c }
  if(k>5 && k<=6)  { r1=c; b1=x }
  
  let m = v-c; 

  return [r1+m,g1+m,b1+m]  
}

// ----------------------------
// TEST code
// ----------------------------

$ = x=>document.querySelector(x);

function calcMaxError() {
    s = colorSat;
    v = colorVal;
    err = [];
    
    for(let h=0; h<361; h++) {
        o = hsv2rgb(h,s,v);
        w = hsv2rgb_wiki(h,s,v); 
        e = Math.max( Math.abs(o[0]-w[0]), Math.abs(o[1]-w[1]), Math.abs(o[2]-w[2]));
        err.push({ h, e });
    }
    
    err.sort((a,b)=> b.e-a.e);
    $('#error').innerHTML = `err=${err[0].e} for hue=${err[0].h}&deg`
}

function setColors(h,s,l) {    
    opt = hsv2rgb(h,s,l);
    wiki = hsv2rgb_wiki(h,s,l); 
    
    chartAA( h, opt[colorIdx]); 
    chartBB( h, wiki[colorIdx]);    
    $('#colA').insertAdjacentHTML('beforeend', `<div class="color" style="background: ${rgbToStyle(opt)}"</div>`);
    $('#colB').insertAdjacentHTML('beforeend',`<div class="color" style="background: ${rgbToStyle(wiki)}"</div>`);    
}

function refresh() {  
  s = colorSat;
  l = colorVal;
  $('#colA').innerHTML = "opt";
  $('#colB').innerHTML = "wiki";
  chartAA(0,0,1); chartBB(0,0,1);                 // clean charts
  let info =  ['R','G','B'][colorIdx] 
   + ` (val: ${colorVal}, sat: ${colorSat})`;  
  $('#labelWiki').innerHTML = "Wiki " + info;
  $('#labelOpt').innerHTML = "Optimized " + info;
  
  for(let h=0; h<361; h++) setColors(h,s,l);
  calcMaxError();
}

function rgbToStyle(c) {
    return `rgb(${c[0]*255},${c[1]*255},${c[2]*255})`
}

function...