HSV to RGB
by Lamik
HTML
<div>Charts showing the hue (Y axis) as function of (X axis) r (red line), g (green line), b (blue line). There is also button for error comparison with wiki version.</div>
<div>R <input type="range" min="0" max="255" value="0" oninput="setC(0,event)"></div>
<div>G <input type="range" min="0" max="255" value="0" oninput="setC(1,event)"></div>
<div>B <input type="range" min="0" max="255" value="0" oninput="setC(2,event)"></div>
<div>Wiki-Opt max err <button onclick="calcErr()" >Calc (~1min, progress in console)</button> <span id="error"></span></div>
<div id="rgb"></div>
<div id="hsvNew"></div>
<div id="hsvWiki"></div>
<div id="labelOpt">New</div>
<svg viewBox="0 0 100 10" class="chart">
<text x="0" y="10" class="axis">0</text>
<text x="0" y="2" class="axis">360 H</text>
<text x="98" y="10" class="axis">1</text>
<polyline id="chartNewRH" fill="none" stroke="#990000" stroke-width="0.2" points=""/>
<polyline id="chartNewGH" fill="none" stroke="#009900" stroke-width="0.2" points=""/>
<polyline id="chartNewBH" fill="none" stroke="#000099" stroke-width="0.2" points=""/>
</svg>
<div id="labelOpt">Wiki</div>
<svg viewBox="0 0 100 10" class="chart">
<text x="0" y="10" class="axis">0</text>
<text x="0" y="2" class="axis">360 H</text>
<text x="98" y="10" class="axis">1</text>
<polyline id="chartWikiRH" fill="none" stroke="#990000" stroke-width="0.2" points=""/>
<polyline id="chartWikiGH" fill="none" stroke="#009900" stroke-width="0.2" points=""/>
<polyline id="chartWikiBH" fill="none" stroke="#000099" stroke-width="0.2" 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: 2px sans-serif;}
#labelOpt { margin-top: 20px }
JavaScript
function rgb2hsv_new(r,g,b) {
let v = Math.max(r,g,b);
let n = v-Math.min(r,g,b);
let h= n && ((v==r) ? (g-b)/n : ((v==g) ? 2+(b-r)/n : 4+(r-g)/n));
return [60*(h<0?h+6:h), v&&n/v, v];
}
function rgb2hsv_wiki(r,g,b) {
let v = Math.max(r,g,b);
let i = Math.min(r,g,b);
let n = v-i;
let s=n/v;
let h= 0;
if(n) {
if(v==r) h=60*( 0+(g-b)/n );
if(v==g) h=60*( 2+(b-r)/n );
if(v==b) h=60*( 4+(r-g)/n );
} else h=0;
return [(h+360)%360,s,v];
}
// ----------------------
// TEST code
// ----------------------
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)];
}
$ = x=>document.querySelector(x);
function calcMaxError() {
lastErr = {e:0};
for(let i=0; i<256; i++) {
console.log(`calculation...${(100*i/255)|0}%`)
for(let j=0; j<256; j++) {
for(let k=0; k<256; k++) {
let c = [i/255, j/255, k/255];
w = rgb2hsv_wiki(...c);
n = rgb2hsv_new(...c);
e = Math.max( Math.abs(n[0]-w[0]), Math.abs(n[1]-w[1]), Math.abs(n[2]-w[2]));
if(lastErr.e<e) lastErr = {e, wiki:w, new:n, c, i ,j, k}
}
}
}
$('#error').innerHTML = JSON.stringify(lastErr);
/*
for(let h=0; h<360; h++) {
w = rgb2hsv_wiki(...color);
n = rgb2hsv_new(...color);
e = Math.max( Math.abs(n[0]-w[0]), Math.abs(n[1]-w[1]), Math.abs(n[2]-w[2]));
err.push({ h, e, w, n });
}
err.sort((a,b)=> b.e-a.e);
$('#error').innerHTML = `err=${err[0].e} for hue=${err[0].h}°
<br>
rgb=${color} <br>
w=${w} <br>
n=${n}
` */
}
function chartH(x,y,c,which,r) {
let a = $(`#chart${which}${c}H`).getAttribute('points') +` ${x*100}, ${(10-y/36)}`;
$(`#chart${which}${c}H`).setAttribute('points',r? "":a);
}
function draw(which) {
chartH(0,0,'R',which,1) // clean
...