Working on a "Ultimate Colour Converter" just as something to do and I can get can the colour models to convert to HSL but for some reason when I convert HSL back, the whole thing breaks. I can't even submit it again.
I have also added a bunch of debug lines in the code where it broke but nothing gets reported to console.
My current project can be found here: http://codepen.io/spedwards/pen/ikJxH or http://jsfiddle.net/spedwards/se5yU/
As the code is really long, I'll refrain from posting the whole thing but just a segment of it.
else if (type == 'hsl') {
hsl = type;console.debug(hsl = type);
/* START RGB */
c = type.slice(4).split(',');console.debug('Removed first 4 chars');
c[2] = c[2].substring(0, c[2].length - 1);console.debug('Removed last bracket');
c = hslToRgb(c[0],c[1],c[2]);console.debug('Convert to RGB');
rgb = 'rgb(' + c.join() + ')';console.debug('Sets RGB');
/* END RGB */
/* START RGBA */
rgba = (rgb.substring(0, rgb.length - 1) + ',1)').splice(3,0,'a');console.debug('Sets RGBA');
/* END RGBA */
/* START HEX */
hex = '#';console.debug('Starts hex');
for (i=0;i<3;i++) {
c[i] = +c[i];console.debug('Coerces to number: ' + i);
hex += (c[i] < 16 ? "0" : "") + c[i].toString(16).toUpperCase();console.debug('Sets hex part: ' + i);
}
/* END HEX */
}
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
I should note, type comes in as hsl(0,0,1) or whatever is being input.
I found what my problem was. I had forgotten to change type == 'hsl' to colourType == 'hsl'.
All works now.
Related
I'm attempting to convert this piece of Javascript to VB.net code, and I'm having some trouble getting it to work. All the JS does is some math/byte operations, so I don't think I'm going beyond the scope of either language. Here the original code, sourced from:
Pimp Trizkit's Colour Shading Code
function shadeColor2(color, percent) {
var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF;
return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1);}
And here's what I've attempted to convert it to:
Public Function LightColor(color As String, percent As Integer) As Color
Dim f As Integer = Convert.ToInt32(color.Substring(1), 16)
Dim t As Integer
If percent < 0 Then
t = 0
Else
t = 255
End If
Dim p As Integer
If percent < 0 Then
p = percent * -1
Else
p = percent
End If
Dim R As Integer = f >> 16
Dim G As Integer = f >> 8 And &HFF
Dim B As Integer = f And &HFF
Dim finalColor As String = "#" + (Convert.ToString( _
(&H1000000 + (Math.Round((t - R) * p) + R) * &H10000 + _
(Math.Round((t - G) * p) + G) * &H100 + _
(Math.Round((t - B) * p) + B))) _
).Substring(1)
Me.txtID.Text = finalColor
Return ColorTranslator.FromHtml(finalColor)
End Function
I would greatly appreciate some help regarding my amateur conversion and whether it could work, I've researched the relevant JS syntax but I'm not sure whether I changed it properly.
After running my code with these parameters:
LightColor("#2980b9", 20)
I obtain a 8-digit hex color (#00642865) which is not even a color-I think I have some problems with the formatting of the hex output.
I'm not exactly sure why your code fails, but I do see some potential pitfalls. The most obvious is the percent parameter. This should be a double or a single as the accepted range is >= -1.0 and <= +1.0.
I've created a simple .net fiddle, available here: https://dotnetfiddle.net/QhowPP
Public Shared Function LightColor(htmlColor As String, percent As Double) As Color
If (String.IsNullOrEmpty(htmlColor)) Then
Throw New ArgumentNullException("htmlColor")
ElseIf ((percent < -1D) Or (percent > +1D)) Then
Throw New ArgumentOutOfRangeException("percent")
End If
Dim c = ColorTranslator.FromHtml(htmlColor)
Dim f = Int32.Parse(htmlColor.Substring(1), Globalization.NumberStyles.AllowHexSpecifier)
Dim t = If((percent < 0), 0, 255)
Dim p = If((percent < 0), (percent * -1), percent)
Dim result = ("#" & CInt(
&H1000000 + (Math.Round((t - c.R) * p) + c.R) *
&H10000 + (Math.Round((t - c.G) * p) + c.G) *
&H100 + (Math.Round((t - c.B) * p) + c.B)
).ToString("X").Substring(1))
Return ColorTranslator.FromHtml(result)
End Function
C#:
public static Color LightColor(String htmlColor, Double percent)
{
if (String.IsNullOrEmpty(htmlColor))
{
throw new ArgumentNullException("htmlColor");
}
else if ((percent < -1D) | (percent > +1D))
{
throw new ArgumentOutOfRangeException("percent");
}
var c = ColorTranslator.FromHtml(htmlColor);
var f = Int32.Parse(htmlColor.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier);
var t = ((percent < 0) ? 0 : 255);
var p = ((percent < 0) ? (percent * -1) : percent);
var result = ("#" + ((Int32)(
0x1000000 + (Math.Round((t - c.R) * p) + c.R) *
0x10000 + (Math.Round((t - c.G) * p) + c.G) *
0x100 + (Math.Round((t - c.B) * p) + c.B)
)).ToString("X").Substring(1));
return ColorTranslator.FromHtml(result);
}
In a JavaScript/jQuery app, I want to set the color of a div element based on an external variable. From low to high values of this variable, you go past the colors green to red. Now, I could do this:
setColor: function(quantity)
{
var color;
if(quantity <= -1000)
{
color = '#00ff00'
}
else if(quantity > -1000 && quantity <= -900)
{
color = '#11ee00'
}
// a million more else if statements
return color;
}
Anything that's -1000 or below is #00ff00 (green), and everything that's +1000 or above is #ff0000 (red), with 0 being #ffff00 (yellow). There's lots of color variations in between these 3 extremes: for example, a value of -950 would be a slightly more redder shade of green than -951.
But isn't there a formula for this kind of stuff, so that I don't end with a 1000 line function?
"A slightly more redder shade of green" becomes yellowish, because the color in the middle between red and green is yellow. So this function returns an RGB-string beeing pure green when value is <= lower limit, red when value >= upper limit and yellow when value is middle (0 in your case), and all shades in between.
var low = -1000, upp = 1000,
mid = (upp + low) / 2, dif = (upp - low) / 2;
function grade(value) {
var r = 255, g = 255, b = 0;
if (value <= low) r = 0;
else if (value >= upp) g = 0;
else if (value < mid) r = Math.round(255 * Math.abs(low - value) / dif);
else if (value > mid) g = Math.round(255 * Math.abs(upp - value) / dif);
return 'rgb(' + r + ',' + g + ',' + b + ')';
}
Adjust vars low and upp to your usecase. The function is easy to adapt to colorchanges between green/cyan/blue or red/purple/blue. If you need a full rainbow some more effort is required.
First of all it is good to separate decoration logic(color value) and set classes for the different colors and set those to the div.
I had a task of that kind and used sin fuctions of every chanel with different amplitude, period and shift for every channel:
var ColorPicker = {
colors: {},
init: function(range){
var ra = 190,
rp = 8/3*range,
rs = range*2/3;
var ga = 190,
gp = range,
gs = 0;
var ba = 150,
bp = 8/3*range,
bs = range;
var pipi = 2*Math.PI;
var r, g, b, w;
for (var i = 0; i < range; i++) {
r = ra*Math.cos(i*pipi/rp + rs*pipi/range);
g = ga*Math.cos(i*pipi/gp + gs*pipi/range);
b = ba*Math.cos(i*pipi/bp + bs*pipi/range);
r = Math.round( ( r > 0 ) ? r : 0 );
g = Math.round( ( g > 0 ) ? g : 0 );
b = Math.round( ( b > 0 ) ? b : 0 );
w = Math.round( i/range*255);
this.colors[i] = {red: r, green: g, blue: b, white: w};
};
},
getColorObj: function(grade){
return this.colors[grade];
},
getRGB: function(grade, coef){
var o = this.colors[grade];
if (!coef){
coef = 1;
}
var r = (Math.round(o.red*coef)>255)?255:Math.round(o.red*coef),
g = (Math.round(o.green*coef)>255)?255:Math.round(o.green*coef),
b = (Math.round(o.blue*coef)>255)?255:Math.round(o.blue*coef);
return 'rgb(' + r + ','
+ g + ','
+ b + ')';
},
// get shades of BW
getW: function(grade){
var o = this.colors[grade];
return 'rgb(' + o.white + ','
+ o.white + ','
+ 0.9*o.white + ')';
},
// opposite to BW
getB: function(grade){
var o = this.colors[grade],
b = 255 - o.white;
// console.log(b);
return 'rgb(' + b + ','
+ b + ','
+ .9*b + ')';
},
};
May need to rewrite it though. Don't think it is an optimal solution now.
There is a color manipulation library that's called TinyColor.
What you want to do, is vary the hue. This would be done like this:
var red = tinycolor("#FF0000").toHsv();
var green = tinycolor("#00FF00").toHsv(), h, col, hex;
h = green.h + (red.h-green.h)*(quantity+1000)/2000;
col = tinycolor({h: h, s: 0.5, v: 0.5});
hex = col.toHex();
See demo here
I have translated the following C++ code:
#include <iostream>
using namespace std;
#define NDIGITS 100
#define LEN (NDIGITS/4+1)*14
long a[LEN];
long b;
long c = LEN;
long d;
long e = 0;
long f = 10000;
long g;
long h = 0;
int main(void) {
cout<<b<<endl;
for(; (b=c-=14) > 0 ;){
for(; --b > 0 ;){
d *= b;
if( h == 0 )
d += 2000*f;
else
d += a[b]*f;
g=b+b-1;
a[b] = d % g;
d /= g;
}
h = printf("%ld",e+d/f);
d = e = d % f;
}
getchar();
return 0;
}
Into JavaScript:
function mod(n, m) {
return ((m % n) + n) % n;
} // mod function to fix javascript modulo bug
function calculate(NDIGITS){
var LEN = (NDIGITS / 4 + 1) * 14,
out = "",
a = [],
b = 0,
c = LEN,
d = 0,
e = 0,
f = 10000,
g = 0,
h = 0;
for( ; a.length != LEN; a.push(0));
for( ; (b=c-=14) > 0 ; ){
for(; --b > 0 ;){
d *= b;
if(h == 0)
d += 2000*f;
else
d += a[b]*f;
g=b+b-1;
a[b] = mod(d, g);
d /= g;
};
h = 4;
out += e + d / f;
d = e = mod(d, f);
};
return out;
};
calculate(100);
The problem is, the C++ (which is correct) output looks like this:
314159265358979323846264338327952884197169399375105820974944592307816406286208998628034825342117067
But the JavaScript (which is wrong) output looks like this:
3141.59265358979345928.3358757688158002.0385670499462603.1996016540431161.44919092773639662.2465149363658988.6127837844255865.38922090756173.61883094848226189.6324225085448150.3443440509899223.2179589088062808.1943642437717982.8973948575671840.86646781354151140.38694447211833938.5632867441137341.458720505086448.7384444661472807.14448220310268936.5521832735086764.9290682040381301.76585926509928223.4135991546457438.115065010927
Where did I mess up in my coding? Thanks for the help.
JavaScript does floating point division.
Arguments exchanged in modulo calculation function.
Here is code that produces the same result as the C++ code provided for the given sample (100) digits:
function mod(m, n) {
return ((m % n) + n) % n;
} // mod function to fix javascript modulo bug
function calculate(NDIGITS) {
var LEN = (NDIGITS / 4 + 1) * 14,
out = "",
a = [],
b = 0,
c = LEN,
d = 0,
e = 0,
f = 10000,
g = 0,
h = 0;
for (; a.length !== LEN; a.push(0));
for (; (b = c -= 14) > 0;) {
for (; --b > 0;) {
d *= b;
if (h === 0) {
d += 2000 * f;
} else {
d += a[b] * f;
}
g = b + b - 1;
a[b] = mod(d, g);
d = Math.floor(d / g);
}
h = Math.floor(e + d / f);
out += h;
h = h.length;
d = e = mod(d, f);
}
return out;
}
console.log(calculate(100));
I have a Javascript code, which I use to bring Night Mode effect on an HTML page...
The code goes something like this-
javascript: (function () {
function RGBtoHSL(RGBColor) {
with(Math) {
var R, G, B;
var cMax, cMin;
var sum, diff;
var Rdelta, Gdelta, Bdelta;
var H, L, S;
R = RGBColor[0];
G = RGBColor[1];
B = RGBColor[2];
cMax = max(max(R, G), B);
cMin = min(min(R, G), B);
sum = cMax + cMin;
diff = cMax - cMin;
L = sum / 2;
if (cMax == cMin) {
S = 0;
H = 0;
} else {
if (L <= (1 / 2)) S = diff / sum;
else S = diff / (2 - sum);
Rdelta = R / 6 / diff;
Gdelta = G / 6 / diff;
Bdelta = B / 6 / diff;
if (R == cMax) H = Gdelta - Bdelta;
else if (G == cMax) H = (1 / 3) + Bdelta - Rdelta;
else H = (2 / 3) + Rdelta - Gdelta; if (H < 0) H += 1;
if (H > 1) H -= 1;
}
return [H, S, L];
}
}
function getRGBColor(node, prop) {
var rgb = getComputedStyle(node, null).getPropertyValue(prop);
var r, g, b;
if (/rgb\((\d+),\s(\d+),\s(\d+)\)/.exec(rgb)) {
r = parseInt(RegExp.$1, 10);
g = parseInt(RegExp.$2, 10);
b = parseInt(RegExp.$3, 10);
return [r / 255, g / 255, b / 255];
}
return rgb;
}
function hslToCSS(hsl) {
return "hsl(" + Math.round(hsl[0] * 360) + ", " + Math.round(hsl[1] * 100) + "%, " + Math.round(hsl[2] * 100) + "%)";
}
var props = ["color", "background-color", "border-left-color", "border-right-color", "border-top-color", "border-bottom-color"];
var props2 = ["color", "backgroundColor", "borderLeftColor", "borderRightColor", "borderTopColor", "borderBottomColor"];
if (typeof getRGBColor(document.documentElement, "background-color") == "string") document.documentElement.style.backgroundColor = "white";
revl(document.documentElement);
function revl(n) {
var i, x, color, hsl;
if (n.nodeType == Node.ELEMENT_NODE) {
for (i = 0; x = n.childNodes[i]; ++i) revl(x);
for (i = 0; x = props[i]; ++i) {
color = getRGBColor(n, x);
if (typeof (color) != "string") {
hsl = RGBtoHSL(color);
hsl[2] = 1 - hsl[2];
n.style[props2[i]] = hslToCSS(hsl);
}
}
}
}
})()
I have saved this as a Bookmarklet in my Bookmark Bar on Google Chrome, but I want this to be automatically applied to every page I load. What should I do to achieve this?
You should write this as a userscript and run it with something like tampermonkey
I use my own library for a lot of stuff, and recently I decided to add gradient functionality, but I've encountered a problem that I seem to remember having a while ago also, and this is the matter of my gradient being slightly off near the end. First, the code in question:
gradient = function(l, g)
{
var r = [], s = [], f = g.length - 1;
for (var x = 0; x < g.length; x++)
g[x] = (typeof(g[x]) == 'string' ? g[x] : g[x].join(','))._replace(['#', ' ', 'rgb(', ')'], ''),
g[x] = (g[x].indexOf(',') != -1
? g[x].split(',')
: g[x].chunk(2).map(function(_)
{
return _.fromBase('hex');
}));
for (var x = 0; x < f; x++)
s[x] = [(g[x][0] - g[x + 1][0]) / (l - 1) * f, (g[x][1] - g[x + 1][2]) / (l - 1) * f, (g[x][2] - g[x + 1][2]) / (l - 1) * f];
for (var x = 0; x < l; x++)
r[x] = '', ([0, 1, 2]).map(function(_)
{
var c = Math.floor(x / (l / (g.length - 1)));
r[x] += (g[c][_] - s[c][_] * (x - (l / (g.length - 1)) * c)).toBase('hex').pad('0', 2);
});
return r;
};
And, of course, my library: http://wimg.co.uk/HJ0X8B.js
Have fun in there! : ) If you think you might be able to help in any way at all, the custom functions I use in the gradient snippet are _replace(), chunk(), map(), and toBase() and fromBase()... as you'll be able to see at this demo page, everything pretty much works (at least in Opera and Firefox) save for the gradient being ever so slightly off at the end (hover to be shown the hex value). For example, calling gradient(50, ['ffffff', 'ffff00', '00ff00']) does indeed create an array of length fifty that contains hexadecimal color values gradually shifting from red to yellow and then to lime, however the last color isn't exactly lime (in this case, it comes out 05ff00); this means that there's something slightly off in the mathematics and not the methodology.
So... anybody willing to wade through the jungle that is the code I find so strangely beautiful to help me arrive at a solution? All assistance is greatly appreciated.
gradient = function(l, g)
{
var r = [], s = [], f = g.length - 1;
for (var x = 0; x < g.length; x++)
g[x] = (typeof(g[x]) == 'string' ? g[x] : g[x].join(','))._replace(['#', ' ', 'rgb(', ')'], ''),
g[x] = (g[x].indexOf(',') != -1
? g[x].split(',')
: g[x].chunk(2).map(function(_)
{
return _.fromBase('hex');
}));
for (var x = 0; x < f; x++)
s[x] = [(g[x][0] - g[x + 1][0]) / (l - 1) * f, (g[x][1] - g[x + 1][1]) / (l - 1) * f, (g[x][2] - g[x + 1][2]) / (l - 1) * f];
for (var x = 0; x < l; x++)
r[x] = '', ([0, 1, 2]).map(function(_)
{
var c = Math.floor(x / (l / (g.length - 1)));
r[x] += (g[c][_] - s[c][_] * (x - ((l-1) / (g.length - 1)) * c)).toBase('hex').pad('0', 2);
});
return r;
};
Use (l-1) instead of l on the last calculation line since you have prepared the s array for l-1 steps not l.
BTW your code is really hard to understand, try to write more understandable and standart code. And do write for loops instead of [0, 1, 2].map(sth).