bahaghari/lib: update and refactor colors.rgb

Now the RGB colorspace object doesn't have the methods built into the
set as that basically screws a lot of things when exporting it to the
output.

Also, the alpha component should be handled nicely now.
This commit is contained in:
Gabriel Arazas 2024-06-04 20:40:29 +08:00
parent 3825c8568c
commit 35873462f3
No known key found for this signature in database
GPG Key ID: 62104B43D00AA360
2 changed files with 101 additions and 41 deletions

View File

@ -5,8 +5,13 @@
{ pkgs, lib, self }: { pkgs, lib, self }:
rec { rec {
/* Generates an RGB colorspace object to be generated with its method for # A bunch of metadata for this implementation. Might be useful if you want to
convenience. # create functions from this subset.
valueMin = 0.0;
valueMax = 255.0;
/* Generates an RGB colorspace object. The point is to provide some
typechecking if the values passed are correct.
Type: RGB :: Attrs -> Attrs Type: RGB :: Attrs -> Attrs
@ -14,23 +19,13 @@ rec {
RGB { r = 242.0; g = 12; b = 23; } RGB { r = 242.0; g = 12; b = 23; }
=> { => {
# The individual red, green, and blue components. # The individual red, green, and blue components.
# And several methods.
__functor = {
toHex = <function>;
lighten = <function>;
};
} }
*/ */
RGB = { r, g, b }@color: RGB = { r, g, b, ... }@color:
assert lib.assertMsg (isRgb color) assert lib.assertMsg (isRgb color)
"bahaghariLib.colors.rgb.RGB: given color does not have valid RGB value"; "bahaghariLib.colors.rgb.RGB: Given object has invalid values (only 0-255 are allowed)";
{ lib.optionalAttrs (color ? a) { inherit (color) a; } // {
inherit r g b; inherit r g b;
__functor = {
toHex = self: toHex color;
lighten = self: lighten color;
};
}; };
/* Returns a boolean to check if it's a valid RGB Nix object or not. /* Returns a boolean to check if it's a valid RGB Nix object or not.
@ -49,12 +44,12 @@ rec {
isRgb { r = 123; g = 123; b = 123; } isRgb { r = 123; g = 123; b = 123; }
=> true => true
*/ */
isRgb = { r, g, b }@color: isRgb = { r, g, b, ... }@color:
let let
isWithinRGBRange = v: self.math.isWithinRange 0 255 v; isWithinRGBRange = v: self.math.isWithinRange valueMin valueMax v;
isValidRGB = v: self.isNumber v && isWithinRGBRange v; isValidRGB = v: self.isNumber v && isWithinRGBRange v;
in colors = [ r g b ] ++ lib.optionals (color ? a) [ color.a ];
lib.lists.all (v: isValidRGB v) (lib.attrValues color); in lib.lists.all (v: isValidRGB v) colors;
/* Converts the color to a 6-digit hex string. Unfortunately, it cannot /* Converts the color to a 6-digit hex string. Unfortunately, it cannot
handle floats very well so we'll have to round these up. handle floats very well so we'll have to round these up.
@ -67,13 +62,30 @@ rec {
*/ */
toHex = { r, g, b, ... }: toHex = { r, g, b, ... }:
let let
r' = self.math.round r; components = builtins.map (c:
g' = self.math.round g; let c' = self.math.round c;
b' = self.math.round b; in self.hex.pad 2 (self.hex.fromDec c')) [ r g b ];
rH = self.hex.pad 2 (self.hex.fromDec r'); in lib.concatStringsSep "" components;
gH = self.hex.pad 2 (self.hex.fromDec g');
bH = self.hex.pad 2 (self.hex.fromDec b'); /* Converts the color to a 8-digit hex string (RGBA). If no `a` component, it
in "${rH}${gH}${bH}"; will be assumed to be at maximum value. Unfortunately, it cannot handle
floats very well so we'll have to round these up.
Type: toHex :: RGB -> String
Example:
toHex' { r = 231; g = 12; b = 21; }
=> "E70C15FF"
toHex' { r = 231; g = 12; b = 21; a = 0; }
=> "E70C1500"
*/
toHex' = { r, g, b, a ? valueMax, ... }:
let
components = builtins.map (c:
let c' = self.math.round c;
in self.hex.pad 2 (self.hex.fromDec c')) [ r g b a ];
in lib.concatStringsSep "" components;
/* Converts a valid hex string into an RGB object. /* Converts a valid hex string into an RGB object.
@ -85,6 +97,12 @@ rec {
fromHex "FFF" fromHex "FFF"
=> { r = 255; g = 255; b = 255; } => { r = 255; g = 255; b = 255; }
fromHex "FFFF"
=> { r = 255; g = 255; b = 255; a = 255; }
fromHex "FFFFFFFF"
=> { r = 255; g = 255; b = 255; a = 255; }
*/ */
fromHex = hex: fromHex = hex:
let let
@ -92,8 +110,15 @@ rec {
r = lib.lists.elemAt hex' 0; r = lib.lists.elemAt hex' 0;
g = lib.lists.elemAt hex' 1; g = lib.lists.elemAt hex' 1;
b = lib.lists.elemAt hex' 2; b = lib.lists.elemAt hex' 2;
in a =
RGB { inherit r g b; }; if lib.lists.length hex' == 4 then
lib.lists.elemAt hex' 3
else
null;
in RGB {
inherit r g b;
${self.optionalNull (a != null) "a"} = a;
};
/* Given a percentage, uniformly lighten the given RGB color. /* Given a percentage, uniformly lighten the given RGB color.
@ -105,11 +130,10 @@ rec {
in in
lighten color 50 lighten color 50
*/ */
lighten = { r, g, b, ... }: percentage: lighten = { r, g, b, ... }:
let percentage:
grow' = c: self.math.grow' 0 255 percentage; let grow' = c: self.math.grow' valueMin valueMax percentage;
in in RGB {
RGB {
r = grow' r; r = grow' r;
g = grow' g; g = grow' g;
b = grow' b; b = grow' b;
@ -134,9 +158,10 @@ rec {
hexMatch = hex: hexMatch = hex:
let let
length = lib.stringLength hex; length = lib.stringLength hex;
genMatch = r: n: lib.concatStringsSep "" (lib.genList (_: "([[:xdigit:]]{${builtins.toString n}})") r); generateRegex = r: n:
nonAlphaGenMatch = genMatch 3; lib.strings.replicate r "([[:xdigit:]]{${builtins.toString n}})";
withAlphaGenMatch = genMatch 4; nonAlphaGenMatch = generateRegex 3;
withAlphaGenMatch = generateRegex 4;
regex = regex =
if (length == 6) then if (length == 6) then
@ -153,8 +178,8 @@ rec {
scale = self.trivial.scale { scale = self.trivial.scale {
inMin = 0; inMin = 0;
inMax = 15; inMax = 15;
outMin = 0; outMin = valueMin;
outMax = 255; outMax = valueMax;
}; };
match = lib.strings.match regex hex; match = lib.strings.match regex hex;

View File

@ -5,7 +5,7 @@ let
# actual results. Also, it will mess up the result comparison since comparing # actual results. Also, it will mess up the result comparison since comparing
# functions is reference-based so it will always fail. # functions is reference-based so it will always fail.
normalizeData = colors: normalizeData = colors:
lib.attrsets.removeAttrs colors [ "__functor" ]; lib.attrsets.removeAttrs colors [ "__functor" "methods" ];
rgbSample = self.colors.rgb.RGB { rgbSample = self.colors.rgb.RGB {
r = 255; r = 255;
@ -15,8 +15,7 @@ let
# A modified version of RGB that normalizes data out-of-the-boxly. # A modified version of RGB that normalizes data out-of-the-boxly.
RGB = colors: normalizeData (self.colors.rgb.RGB colors); RGB = colors: normalizeData (self.colors.rgb.RGB colors);
in in lib.runTests {
lib.runTests {
testsBasicRgb = { testsBasicRgb = {
expr = RGB { expr = RGB {
r = 34; r = 34;
@ -30,6 +29,21 @@ lib.runTests {
}; };
}; };
testsBasicRgb2 = {
expr = RGB {
r = 23;
g = 65;
b = 241;
a = 255;
};
expected = {
r = 23;
g = 65;
b = 241;
a = 255;
};
};
testsFromHex = { testsFromHex = {
expr = normalizeData (self.colors.rgb.fromHex "FFFFFF"); expr = normalizeData (self.colors.rgb.fromHex "FFFFFF");
expected = normalizeData (self.colors.rgb.RGB { expected = normalizeData (self.colors.rgb.RGB {
@ -54,6 +68,7 @@ lib.runTests {
r = 255; r = 255;
g = 255; g = 255;
b = 255; b = 255;
a = 255;
}); });
}; };
@ -63,6 +78,7 @@ lib.runTests {
r = 255; r = 255;
g = 255; g = 255;
b = 255; b = 255;
a = 255;
}); });
}; };
@ -80,6 +96,25 @@ lib.runTests {
expected = "173A69"; expected = "173A69";
}; };
testsToHexVariant = {
expr = self.colors.rgb.toHex' (RGB {
r = 255;
g = 56;
b = 105;
});
expected = "FF3869FF";
};
testsToHexVariant2 = {
expr = self.colors.rgb.toHex' (RGB {
r = 255;
g = 56;
b = 105;
a = 34;
});
expected = "FF386922";
};
testsHexMatch = { testsHexMatch = {
expr = self.colors.rgb.hexMatch "FFF"; expr = self.colors.rgb.hexMatch "FFF";
expected = [ 255 255 255 ]; expected = [ 255 255 255 ];