rgb values to 0 to 1 scale - math

I'm trying to calculate some RGB colors (0 - 255) for a 0 to 1 scale. Does anyone knows a online converter or it exists a math formula?
Lets say that I want to convert 125 RGB with (0 to 255 scale) to a 0 to 1 scale.

It's simply a case of dividing your RGB value, call it x by 255:
If x = 95 then your value is 95/255 = 0.373 (to 3 d.p.)

a = x / 255
or
x = a * 255
where x is the RGB value and a is your desired result.

if you want to continue using 0-255 can create a function for this as the following example.
function setFillColor2(...)
local object,r,b,g=...
object:setFillColor( r/255, b/255,g/255)
end
circle1 = display.newCircle(150,250, 50 )
setFillColor2(circle1,23,255,12)

The simplest way if to divide by either 255 for 0-1 or 256 for 0 - a bit less than 1. The latter sometimes has advantages. You need to do the divisions in fixed or floating point, not in integer arithmetic, of course.
However in fact the human response to an rgb channel value on 0-255 is not linear, neither is the device's response. Often you need to do gamma correction. It all gets very involved very quickly, and it doesn't usually matter much for low end graphics. For high end graphics, however, you often want to be out of rgb colourspace altogether, and then you need non-linear conversion functions to finally flush the rgb pixels to the end image.

use like this
color: rgb(140 / 255, 180 / 255, 205 / 255),
divide 255 to each actual value , its always falls between 0-1 scale
Actual RGB Color : 140 180 205
0-1 scale code - rgb(140 / 255, 180 / 255, 205 / 255)

Related

How to construct a linear map from floats in [0,1] to integers in [0,255]?

I need to convert a 32 bit floating point value x in the range [0,1] to an 8 bit unsigned integer y in the range [0,255].
A formula I found in some C code is : y = (uint8)(255.99998f*x).
This provides the required conversion, but there is a problem with it.
Conversion of 0.75 yield 191, and conversion of 0.25 yields 63. While 0.75+0.25 = 1, 191+63 = 254 and not the desired 255.
Same problem with 0.5 that is converted into 127. 0.5 + 0.5 = 1 and 127+127= 254 instead of 255.
There is thus a rounding error.
Can this be avoided ? If yes, how ?
You will not be able to represent the closed segment [0.0, 1.0] in an accurate way into the segment [0,255]. The most evident problem is that 0.5 + 0.5 = 1.0 . So if 1.0 is represented by 255, 0.5 cannot be exactly represented.
The real problem is that 32 bits floating point numbers are represented in IEE 754 binary 32 format. So you will find a native injection from the [0.0, 1.0[ semi open segment into the [0,255] one by taking the most representative bits of the binary representation (conveniently shifted) and accepting that at the limit 1.0 would be represented as 256.
Then all fractions where the denominator is a power of 2 are exactly represented: 0.5 is 128, 0.25 is 64, and 0.75 is 192 but trying to nicely map [0.0, 1.0] to [0, 255] is close to finding a nice relation from [0,256] (257 values) into [0,255]...
Same problem with 0.5 that is converted into 127. 0.5 + 0.5 = 1 and 127+127= 254 instead of 255.
No mapping can satisfy this requirement since 255/2 is not representable as an integer. You have to decide what this mapping means to you and what properties it requires, but no mapping to integers can satisfy this.
If you choose a floor mapping as you've shown in your question, then 0.5f->127, in which case your algorithm or program might interpret this to define the range of [0-127] with 128 elements - exactly half of the 256 elements in [0-255], since the remaining range [128-255] also has 128 elements.
If, however, you choose an analytical mapping like
y = round(255*x);
this provides the most accurate numerical value - the value of the output will always be the closest integer to the input value. For a value of 0.5f, this produces 128, which is exactly half of the number of bins in the output range. In this case your algorithm might interpret this as the number of elements in the range which is half of the input range. It's really up to you to design the algorithm and interpretation of the mapping around the limitations imposed by discarding the resolution of a 32-bit float.
Ultimately, [0.0-1.0] is about measuring something and [0-255] is about counting something... only you know what you're measuring and what you're counting so we can't really make this decision for you.
If your application is one which is measurement-like, then round(255*x) will produce the closest integer to the input float - a value of 0.0039062, for example, is within 0.001% of a perfect map to 1, will map to 1.
If your application is one which is counting-like, and you are more interested in equally binning the float values, then a floor mapping (like your original suggestion) will map an equal range of the input to each bin. Using the round equation will leave the 0 bin and the 255 bin mapped to half the range of the rest of the bins. Using a floor mapping produces an equal distribution of the input range to the output bins, but sacrifices numerical precision. The above example of a value of 0.0039062, for example, would map to 0 in this case, even though it's 99.99% of the value you would consider to be 1.
It's entirely up to you to determine which mapping makes sense for your specific application.

Scaling a color based on a target contrast ratio

I am trying to create a Sass function that receives a foreground color and background color and calculates the contrast ratio. From there (and the part I'm stuck on) is that it would simply return the foreground color if it meets the target contrast ratio, but if it doesn't it would lighten or darken the foreground color to meet the target contrast ratio.
For example, if the background supplied was #000 and the foreground supplied was #444 (a contrast ratio of 2.15), this function would lighten the foreground to #757575 and return that color.
I've got everything working except for the part where I need to reverse the contrast calculation. My initial thought was to approach it with what percentage it was away from target and simply lighten/darken (depending on which color was originally darker) by 100 minus the percent difference. This approach, in hindsight, was a little naive and I'm afraid some more advanced math will be involved.
Here is what I created so far (and here is a simplified fiddle):
#function wcag-color($bg, $fg, $size: 16px, $level: "aa"){
#if ( $level == "aa" ){
$wcag_contrast_ratio: 4.5; //For text smaller than 18px
#if ( $size >= 19 ){
$wcag_contrast_ratio: 3; //For text larger than 19px
}
}
#if ( $level == "aaa" ){
$wcag_contrast_ratio: 7; //For text smaller than 18px
#if ( $size >= 19 ){
$wcag_contrast_ratio: 4.5; //For text larger than 19px
}
}
$actual_contrast_ratio: contrast($bg, $fg); //This function returns the contrast between the two colors.
#if ( $actual_contrast_ratio > $wcag_contrast_ratio ){
#return $fg; //Foreground color is acceptable
}
//Scale the lightness of the foreground to meet requested WCAG contrast ratio
$difference: 100 - $actual_contrast_ratio / $wcag_contrast_ratio * 100; //There is more to it than this...
//Edit: here are a few new lines to ponder. This assumes BG is darker than FG (would need to add a condition to compare luminance of each).
$acceptable_luminance: luminance($bg)*$wcag_contrast_ratio; //What the luminance of the FG must be to comply
$difference: ($acceptable_luminance - luminance($fg)); //How far away the FG luminance actually is (not sure if this helps anything...)
#return scale-color($fg, $lightness: $difference); //Unfortunately luminance is not the same as lightness.
}
Notice the commented line "There is more to it than this..." – that is where I need to reverse my contrast formula, but I'd love if there was a simpler formula to use since I already know what the target contrast ratio is.
I've been thinking about this for a few days an I'm stumped. I'd prefer to avoid a guess-and-check method by looping through 1% lightened/darkened colors and testing each individually for their contrast ratio– that would work, but I'm sure there is a more optimal solution.
This was my reference for my initial functions (contrast and luminance) and was very helpful: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
Note: I am not using Compass or any other Sass libraries.
Edit: Here is a simplified fiddle for reference: https://www.sassmeister.com/gist/445836123feb42885a0cf7f4709261ff
So given #000000 and #444444, you can calculate the contrast ratio (2.15 in this case). The math is pretty straightforward, albeit a little hairy. (See the "relative luminance" definition.)
Now you want to go backwards? If you have #000000 and want a ratio of 4.5, starting with #444444, what should the color be? Is that what
I need to reverse my contrast formula
means?
It's a little complicated because you're solving for 3 variables, the red, green, and blue components, plus the luminance formula doesn't treat the red, green and blue equally. It's using 21.25% red, 71.5% green, and 7.25% blue.
Plus, the luminance formula isn't a simple linear formula so you can't just take a percentage short of luminance and bump the color value by that same percentage.
For example, in your case, the ratio was 2.15 but you need it to be 4.5. 2.15 is 108% short of 4.5, the desired value.
However, if you look at your original RGB values #444444 and you calculated it needed to be #757575 (in order to have a 4.5 ratio), then if you treat those RGB values as simple numbers (and convert to decimal), then #444444 (4473924) is 72% short of #757575 (7697781).
So you have a disconnect that your ratio is short by 108% but your RGB values are short by 72%. Thus you can't do a simple linear equation.
(The numbers aren't quite exact since #757575 gives you a 4.56 ratio, not an exact 4.5 ratio. If you use #747474, you get a 4.49 ratio, which is just a smidge too small for WCAG compliance but is closer to 4.5 than 4.56. However, #444444 is 71% short of #747474, so it's still not the same as 2.15 being 108% short of 4.5, so the basic concept still applies.)
Just for fun, I looked at the values of 0x11111 through 0x666666, incrementing by 0x111111, and calculated the contrast ratio. There weren't enough points on the graph so I added a color halfway between 0x111111 and 0x222222, then halfway between 0x222222 and 0x333333, etc.
RGB contrast % from 4.5 % from 0x747474
111111 1.11 305.41% 582.35%
191919 1.19 278.15% 364.00%
222222 1.32 240.91% 241.18%
2a2a2a 1.46 208.22% 176.19%
333333 1.66 171.08% 127.45%
3b3b3b 1.87 140.64% 96.61%
444444 2.16 108.33% 70.59%
4c4c4c 2.45 83.67% 52.63%
555555 2.82 59.57% 36.47%
5d5d5d 3.19 41.07% 24.73%
666666 3.66 22.95% 13.73%
6e6e6e 4.12 9.22% 5.45%
As you can see, the lines interset at the 3rd data point then converge toward each other. I'm sure there's a formula in there so you could take the contrast percentage, do some (probably logarithmic) function on it and get the percentage needed to change the color.
This would be a fascinating math problem that I currently don't have time to play with.
Update Jan 18, 2019
I got it to work going backwards, but it doesn't handle edge cases such as when making a dark color darker but you've already reached the max (or a light color lighter but you reached the max). But maybe you can play with it.
Test case
#ee0add for the light color (magenta-ish)
#445566 for the dark color (dark gray)
contrast ratio 2.09
When computing the "relative luminance" of a color, it has a conditional statement.
if X <= 0.03928 then
X = X/12.92
else
X = ((X+0.055)/1.055) ^ 2.4
Before X is used in that condition, it's divided by 255 to normalize the value between 0 and 1. So if you take the conditional value, 0.03928, and multiply by 255, you get 10.0164. Since RGB values must be integers, that means an RGB component of 10 (0x0A) or less will go through the "if" and anything 11 (0x0B) or bigger will go through the "else". So in my test case values, I wanted one of the color parts to be 10 (0x0A) (#EE0ADD).
The relative luminance for #ee0add is 0.23614683378171950172526363525113 (0.236)
The relative luminance for #445566 is 0.0868525191131797135799815832377 (0.0868)
The "contrast ratio" is
(0.236 + .05) / (0.0868 + .05) = 2.09
(You can verify this ratio on https://webaim.org/resources/contrastchecker/?fcolor=EE0ADD&bcolor=445566)
If we want a ratio of 4.5, and we want #ee0add to not change, then we have to adjust #445566. That means you need to solve for:
4.5 = (0.236 + .05) / (XX + .05)
So the second luminance value (XX) needs to be 0.01358818528482655593894747450025 (0.0136)
The original second luminance value was 0.0868525191131797135799815832377 (0.0868), so to get 0.01358818528482655593894747450025 (0.0136), we need to multiply the original by 0.15645125119651910313960717062698 (0.0136 / 0.0868 = 0.156) (or 15.6% of the original value)
If we apply that 15.6% to each of the R, G, and B relative luminance values, and then work through the conditional statement above backwards, you can get the RGB values.
Original luminance for #445566
r = 0x44 = 68
g = 0x55 = 85
b = 0x66 = 102
r1 = 68 / 255 = 0.26666666666666666666666666666667
g1 = 85 / 255 = 0.33333333333333333333333333333333
b1 = 102 / 255 = 0.4
r2 = ((.267 + .055) / 1.055)^^2.4 = 0.05780543019106721120703816752337
g2 = ((.333 + .055) / 1.055)^^2.4 = 0.09084171118340767766490119106965
b2 = ((.400 + .055) / 1.055)^^2.4 = 0.13286832155381791428570549818868
l = 0.2126 * r2 + 0.7152 * g2 + 0.0722 * b2
= 0.0868525191131797135799815832377
Working backwards, take 15.6% of the r2, g2, and b2 values
r2 = 0.05780543019106721120703816752337 * 15.6% = 0.00904373187934550551617004082875
g2 = 0.09084171118340767766490119106965 * 15.6% = 0.01421229937547695322310904970549
b2 = 0.13286832155381791428570549818868 * 15.6% = 0.02078741515147623990363062978804
Now undo that mess with ^^2.4 and the other stuff
to undo X^^2.4 you have to do the inverse, X^^(1/2.4) or X^^(0.4167)
then multiply by 1.055
then subtract 0.055
then multiply by 255
pow( 0.00904373187934550551617004082875, 1/2.4) = 0.14075965680504652191078668676178
pow( 0.01421229937547695322310904970549, 1/2.4) = 0.16993264267137740728089791717873
pow( 0.02078741515147623990363062978804, 1/2.4) = 0.19910562853770829265100914759565
multiply by 1.055
0.14075965680504652191078668676178 * 1.055 = 0.14850143792932408061587995453368
0.16993264267137740728089791717873 * 1.055 = 0.17927893801830316468134730262356
0.19910562853770829265100914759565 * 1.055 = 0.21005643810728224874681465071341
subtract 0.055
0.14850143792932408061587995453368 - 0.055 = 0.09350143792932408061587995453368
0.17927893801830316468134730262356 - 0.055 = 0.12427893801830316468134730262356
0.21005643810728224874681465071341 - 0.055 = 0.15505643810728224874681465071341
multiply by 255
0.09350143792932408061587995453368 * 255 = 23.842866671977640557049388406088 = 24 = 0x18
0.12427893801830316468134730262356 * 255 = 31.691129194667306993743562169008 = 32 = 0x20
0.15505643810728224874681465071341 * 255 = 39.53939171735697343043773593192 = 40 = 0x28
So the darker color is #182028. There are probably some rounding errors but if you check the original foreground color, #ee0add with the new color, #182028, you get a contrast ratio of 4.48. Just shy of 4.5, but like I said, probably some rounding errors.
https://webaim.org/resources/contrastchecker/?fcolor=EE0ADD&bcolor=182028
I tried doing the same thing with #ee0add, keeping #445566 the same, but when going backwards and getting to the last step where you multiply by 255, I got numbers greater than 255, which are not valid RGB components (they can only go up to 0xFF). If I stopped the number at 255 then took the difference and added it to the smallest color value, I got a decent color but the ratio was 5.04, overshooting 4.5. I can post that math too if you want.

Difference between complement and invert in SASS?

I was going through SASS documentation and found that complement and invert have the same output, Can you tell me what is the difference between these two?
//SASS Code
$color:#ff0099;
$complement:complement($color);
//Returns the complement of a color.
$invert:invert($color);//Returns the inverse of a color.
.complement{background:$complement;}
.invert{background:$invert;}
//CSS
.complement {
background: #00ff66;//Same Color Code
}
.invert {
background: #00ff66;//Same Color Code
}
For some reason, many online examples for complement/invert use color values resulting in the same output for both functions.
While the complement/invert of many color values are the same, there are also many values that result in different colors.
Example:
$color: #ff6699;
complement($color) = #66ffcc;
invert($color) = #009966;
To re-word the Sass documentation:
Complement
Returns the color that is 180 degrees opposite on the HSL color wheel.
To calculate the complement of a color:
Convert the color value to RGB
#ff6699 = RGB 255, 102, 153
Add the highest and lowest RGB values
255 + 102 = 357
Subtract each of the original RGB values from the number in step #2
(357-255) (357-102) (357-153)
102 255 204
This corresponds to #66ffcc
Invert
Returns the inverted red, green, and blue values of the color.
To calculate the invert of a color:
Convert the color value to RGB
#ff6699 = RGB 255, 102, 153
Flip the values by subtracting the original RGB values from 255
(255-255) (255-102) (255-153)
0 153 102
This corresponds to #009966

Convert a large int to a float between 0.0f and 1.0f

I'm working on a project where I have a pane that goes from 0 to 500 on the x and z, but I need to convert that coordinate (from 0 to 500) to a float in the range of 0.0 to 1.0 in because it is required from some of the functions I'm using.
I'm stuck on how to take the axis position (lets say something like, 3475x3475) and change in into it's relative position on the plane from 0.0f to 1.0f
(I've tried to write this in a way that any programmer can understand, instead of using Unity terminology. If you're a Unity programmer and would better understand it with that terminology tell me and I'll re-write it)
You can take your number that ranges from 0 to 500 and simply divide it by 500, e.g. scaled_x = x / 500.0f. Depending on the language and the type of x you will need to divide by either 500 or 500.0f. If you are using a language that has integer division like C, and if x is an integer than x / 500 will be zero unless x is 500, but if you do x/500.0f than you will get a float between 0.0f and 1.0f (assuming x is between 0 and 500 inclusively). This is of course assuming you want a linear mapping, e.g. 0 maps to 0, 100 maps to 0.2, 250 maps to 0.5 and so on.

Convert arbitrary length to a value between -1.0 a 1.0?

How can I convert a length into a value in the range -1.0 to 1.0?
Example: my stage is 440px in length and accepts mouse events. I would like to click in the middle of the stage, and rather than an output of X = 220, I'd like it to be X = 0. Similarly, I'd like the real X = 0 to become X = -1.0 and the real X = 440 to become X = 1.0.
I don't have access to the stage, so i can't simply center-register it, which would make this process a lot easier. Also, it's not possible to dynamically change the actual size of my stage, so I'm looking for a formula that will translate the mouse's real X coordinate of the stage to evenly fit within a range from -1 to 1.
-1 + (2/440)*x
where x is the distance
So, to generalize it, if the minimum normalized value is a and the maximum normalized value is b (in your example a = -1.0, b = 1.0 and the maximum possible value is k (in your example k = 440):
a + x*(b-a)/k
where x is >= 0 and <= k
This is essentially two steps:
Center the range on 0, so for example a range from 400 to 800 moves so it's from -200 to 200. Do this by subtracting the center (average) of the min and max of the range
Divide by the absolute value of the range extremes to convert from a -n to n range to a -1 to 1 range. In the -200 to 200 example, you'd divide by 200
Doesn't answer your question, but for future googlers looking for a continuous monotone function that maps all real numbers to (-1, 1), any sigmoid curve will do, such as atan or a logistic curve:
f(x) = atan(x) / (pi/2)
f(x) = 2/(1+e-x) - 1
(x - 220) / 220 = new X
Is that what you're looking for?
You need to shift the origin and normalize the range. So the expression becomes
(XCoordinate - 220) / 220.0
handling arbitrary stage widths (no idea if you've got threads to consider, which might require mutexes or similar depending on your language?)
stageWidth = GetStageWidth(); // which may return 440 in your case
clickedX = MouseInput(); // should be 0 to 440
x = -1.0 + 2.0 * (clickedX / stageWidth); // scale to -1.0 to +1.0
you may also want to limit x to the range [-1,1] here?
if ( x < -1 ) x = -1.0;
if ( x > 1 ) x = 1.0;
or provide some kind of feedback/warning/error if its out of bounds (only if it really matters and simply clipping it to the range [-1,1] isn't good enough).
You have an interval [a,b] that you'd like to map to a new interval [c,d], and a value x in the original coordinates that you'd like to map to y in the new coordinates. Then:
y = c + (x-a)*(c-d)/(b-a)
And for your example with [a,b] = [0,440] and [c,d] = [-1,1], with x=220:
y = -1 + (220-0)*(1 - -1)/(440-0)
= 0
and so forth.
By the way, this works even if x is outside of [a,b]. So as long as you know any two values in both systems, you can convert any value in either direction.

Resources