mirror of
https://github.com/foo-dogsquared/nixos-config.git
synced 2025-02-07 06:19:00 +00:00
bahaghari/lib: init colors.rgb
subset
This commit is contained in:
parent
f882c30209
commit
c15ec954c0
8
subprojects/bahaghari/lib/colors/README.adoc
Normal file
8
subprojects/bahaghari/lib/colors/README.adoc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
= Bahaghari library: Colors subset
|
||||||
|
:toc:
|
||||||
|
|
||||||
|
The colors subset of the Bahaghari library.
|
||||||
|
It's not on the level of https://www.colour-science.org/[Color Science for Python] or https://crates.io/crates/palette[palette library for Rust] where it gives you a complete toolkit for manipulating a lot of aspects of colors.
|
||||||
|
Rather, this is only in relation to generating them colors to be pressed onto a template like program configurations from Nix modules and even some light templating for some.
|
||||||
|
Its API should only limit on that aspect.
|
||||||
|
No way in hell we're trying to reimplment that in Nix, lol.
|
167
subprojects/bahaghari/lib/colors/rgb.nix
Normal file
167
subprojects/bahaghari/lib/colors/rgb.nix
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# The most antiquated colorspace like ever. For this implementation, we will be
|
||||||
|
# looking after the RGB specification (especially the hexadecimal notation) as
|
||||||
|
# specified from W3 CSS Color Module Level 4
|
||||||
|
# (https://www.w3.org/TR/css-color-4) since it is the most common one.
|
||||||
|
{ pkgs, lib, self }:
|
||||||
|
|
||||||
|
rec {
|
||||||
|
/* Generates an RGB colorspace object to be generated with its method for
|
||||||
|
convenience.
|
||||||
|
|
||||||
|
Type: RGB :: Attrs -> Attrs
|
||||||
|
|
||||||
|
Example:
|
||||||
|
RGB { r = 242.0; g = 12; b = 23; }
|
||||||
|
=> {
|
||||||
|
# The individual red, green, and blue components.
|
||||||
|
|
||||||
|
# And several methods.
|
||||||
|
__functor = {
|
||||||
|
toHex = <function>;
|
||||||
|
lighten = <function>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
RGB = { r, g, b }@color:
|
||||||
|
assert lib.assertMsg (isRgb color)
|
||||||
|
"bahaghariLib.colors.rgb.RGB: given color does not have valid RGB value";
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
|
||||||
|
Type: isRgb :: Attrs -> Bool
|
||||||
|
|
||||||
|
Example:
|
||||||
|
isRgb { r = 34; g = 43; b = 555; }
|
||||||
|
# `b` is more than 255.0 so it's a false
|
||||||
|
=> false
|
||||||
|
|
||||||
|
isRgb { r = 123; g = null; b = 43; }
|
||||||
|
# `g` is not a number so it's'a false again
|
||||||
|
=> false
|
||||||
|
|
||||||
|
isRgb { r = 123; g = 123; b = 123; }
|
||||||
|
=> true
|
||||||
|
*/
|
||||||
|
isRgb = { r, g, b }@color:
|
||||||
|
let
|
||||||
|
isWithinRGBRange = v: self.math.isWithinRange 0 255 v;
|
||||||
|
isValidRGB = v: self.isNumber v && isWithinRGBRange v;
|
||||||
|
in
|
||||||
|
lib.lists.all (v: isValidRGB v) (lib.attrValues color);
|
||||||
|
|
||||||
|
/* Converts the color to a 6-digit hex string. 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; }
|
||||||
|
=> "E70C15"
|
||||||
|
*/
|
||||||
|
toHex = { r, g, b, ... }:
|
||||||
|
let
|
||||||
|
r' = self.math.round r;
|
||||||
|
g' = self.math.round g;
|
||||||
|
b' = self.math.round b;
|
||||||
|
rH = self.hex.pad 2 (self.hex.fromDec r');
|
||||||
|
gH = self.hex.pad 2 (self.hex.fromDec g');
|
||||||
|
bH = self.hex.pad 2 (self.hex.fromDec b');
|
||||||
|
in "${rH}${gH}${bH}";
|
||||||
|
|
||||||
|
/* Converts a valid hex string into an RGB object.
|
||||||
|
|
||||||
|
Type: fromHex :: String -> RGB
|
||||||
|
|
||||||
|
Example:
|
||||||
|
fromHex "FFFFFF"
|
||||||
|
=> { r = 255; g = 255; b = 255; }
|
||||||
|
|
||||||
|
fromHex "FFF"
|
||||||
|
=> { r = 255; g = 255; b = 255; }
|
||||||
|
*/
|
||||||
|
fromHex = hex:
|
||||||
|
let
|
||||||
|
hex' = hexMatch hex;
|
||||||
|
r = lib.lists.elemAt hex' 0;
|
||||||
|
g = lib.lists.elemAt hex' 1;
|
||||||
|
b = lib.lists.elemAt hex' 2;
|
||||||
|
in
|
||||||
|
RGB { inherit r g b; };
|
||||||
|
|
||||||
|
/* Given a percentage, uniformly lighten the given RGB color.
|
||||||
|
|
||||||
|
Type: lighten :: RGB -> Number -> RGB
|
||||||
|
|
||||||
|
Example:
|
||||||
|
let
|
||||||
|
color = RGB { r = 12; g = 46; b = 213; };
|
||||||
|
in
|
||||||
|
lighten color 50
|
||||||
|
*/
|
||||||
|
lighten = { r, g, b, ... }: percentage:
|
||||||
|
let
|
||||||
|
grow' = c: self.math.grow' 0 255 percentage;
|
||||||
|
in
|
||||||
|
RGB {
|
||||||
|
r = grow' r;
|
||||||
|
g = grow' g;
|
||||||
|
b = grow' b;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Given an RGB color in hexadecimal notation, returns a list of integers
|
||||||
|
representing each of the components in order. Certain forms of hex strings
|
||||||
|
will also return a fourth component representing the alpha channel (RGBA).
|
||||||
|
|
||||||
|
Type: hexMatch :: String -> List
|
||||||
|
|
||||||
|
Example:
|
||||||
|
hexMatch "FFF"
|
||||||
|
=> [ 255 255 255 ]
|
||||||
|
|
||||||
|
hexMatch "FFFF"
|
||||||
|
=> [ 255 255 255 255 ]
|
||||||
|
|
||||||
|
hexMatch "0A0B0C0D"
|
||||||
|
=> [ 10 11 12 13 ]
|
||||||
|
*/
|
||||||
|
hexMatch = hex:
|
||||||
|
let
|
||||||
|
length = lib.stringLength hex;
|
||||||
|
genMatch = r: n: lib.concatStringsSep "" (lib.genList (_: "([[:xdigit:]]{${builtins.toString n}})") r);
|
||||||
|
nonAlphaGenMatch = genMatch 3;
|
||||||
|
withAlphaGenMatch = genMatch 4;
|
||||||
|
|
||||||
|
regex =
|
||||||
|
if (length == 6) then
|
||||||
|
nonAlphaGenMatch 2
|
||||||
|
else if (length == 3) then
|
||||||
|
nonAlphaGenMatch 1
|
||||||
|
else if (length == 8) then
|
||||||
|
withAlphaGenMatch 2
|
||||||
|
else if (length == 4) then
|
||||||
|
withAlphaGenMatch 1
|
||||||
|
else
|
||||||
|
throw "Not a valid hex code";
|
||||||
|
|
||||||
|
scale = self.trivial.scale {
|
||||||
|
inMin = 0;
|
||||||
|
inMax = 15;
|
||||||
|
outMin = 0;
|
||||||
|
outMax = 255;
|
||||||
|
};
|
||||||
|
|
||||||
|
match = lib.strings.match regex hex;
|
||||||
|
output = builtins.map (x: self.hex.toDec x) match;
|
||||||
|
in
|
||||||
|
if (length == 3 || length == 4) then
|
||||||
|
builtins.map (x: scale x) output
|
||||||
|
else
|
||||||
|
output;
|
||||||
|
}
|
@ -30,6 +30,12 @@ pkgs.lib.makeExtensible
|
|||||||
hex = callLibs ./hex.nix;
|
hex = callLibs ./hex.nix;
|
||||||
math = callLibs ./math.nix;
|
math = callLibs ./math.nix;
|
||||||
|
|
||||||
|
# We won't export any of the attributes here as a top-level attribute for
|
||||||
|
# some unbeknownst and probably irrational reason.
|
||||||
|
colors = {
|
||||||
|
rgb = callLibs ./colors/rgb.nix;
|
||||||
|
};
|
||||||
|
|
||||||
# Dedicated module sets are not supposed to have any of its functions as a
|
# Dedicated module sets are not supposed to have any of its functions as a
|
||||||
# top-level attribute. It's to make things a bit easier to organize and
|
# top-level attribute. It's to make things a bit easier to organize and
|
||||||
# maintain. Plus, if there's any functions that are easily applicable
|
# maintain. Plus, if there's any functions that are easily applicable
|
||||||
|
@ -12,4 +12,5 @@ in
|
|||||||
math = callLib ./math.nix;
|
math = callLib ./math.nix;
|
||||||
trivial = callLib ./trivial;
|
trivial = callLib ./trivial;
|
||||||
tinted-theming = callLib ./tinted-theming;
|
tinted-theming = callLib ./tinted-theming;
|
||||||
|
rgb = callLib ./rgb.nix;
|
||||||
}
|
}
|
||||||
|
102
subprojects/bahaghari/tests/lib/rgb.nix
Normal file
102
subprojects/bahaghari/tests/lib/rgb.nix
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
{ pkgs, lib, self }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# A modified version that simply removes the functor to focus more on the
|
||||||
|
# actual results. Also, it will mess up the result comparison since comparing
|
||||||
|
# functions is reference-based so it will always fail.
|
||||||
|
normalizeData = colors:
|
||||||
|
lib.attrsets.removeAttrs colors [ "__functor" ];
|
||||||
|
|
||||||
|
rgbSample = self.colors.rgb.RGB {
|
||||||
|
r = 255;
|
||||||
|
g = 255;
|
||||||
|
b = 255;
|
||||||
|
};
|
||||||
|
|
||||||
|
# A modified version of RGB that normalizes data out-of-the-boxly.
|
||||||
|
RGB = colors: normalizeData (self.colors.rgb.RGB colors);
|
||||||
|
in
|
||||||
|
lib.runTests {
|
||||||
|
testsBasicRgb = {
|
||||||
|
expr = RGB {
|
||||||
|
r = 34;
|
||||||
|
g = 2;
|
||||||
|
b = 0;
|
||||||
|
};
|
||||||
|
expected = {
|
||||||
|
r = 34;
|
||||||
|
g = 2;
|
||||||
|
b = 0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testsFromHex = {
|
||||||
|
expr = normalizeData (self.colors.rgb.fromHex "FFFFFF");
|
||||||
|
expected = normalizeData (self.colors.rgb.RGB {
|
||||||
|
r = 255;
|
||||||
|
g = 255;
|
||||||
|
b = 255;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
testsFromHex2 = {
|
||||||
|
expr = normalizeData (self.colors.rgb.fromHex "FFF");
|
||||||
|
expected = normalizeData (self.colors.rgb.RGB {
|
||||||
|
r = 255;
|
||||||
|
g = 255;
|
||||||
|
b = 255;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
testsFromHex3 = {
|
||||||
|
expr = normalizeData (self.colors.rgb.fromHex "FFFF");
|
||||||
|
expected = normalizeData (self.colors.rgb.RGB {
|
||||||
|
r = 255;
|
||||||
|
g = 255;
|
||||||
|
b = 255;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
testsFromHex4 = {
|
||||||
|
expr = normalizeData (self.colors.rgb.fromHex "FFFFFFFF");
|
||||||
|
expected = normalizeData (self.colors.rgb.RGB {
|
||||||
|
r = 255;
|
||||||
|
g = 255;
|
||||||
|
b = 255;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
testsToHex = {
|
||||||
|
expr = self.colors.rgb.toHex rgbSample;
|
||||||
|
expected = "FFFFFF";
|
||||||
|
};
|
||||||
|
|
||||||
|
testsToHex2 = {
|
||||||
|
expr = self.colors.rgb.toHex (RGB {
|
||||||
|
r = 23;
|
||||||
|
g = 58;
|
||||||
|
b = 105;
|
||||||
|
});
|
||||||
|
expected = "173A69";
|
||||||
|
};
|
||||||
|
|
||||||
|
testsHexMatch = {
|
||||||
|
expr = self.colors.rgb.hexMatch "FFF";
|
||||||
|
expected = [ 255 255 255 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testsHexMatch2 = {
|
||||||
|
expr = self.colors.rgb.hexMatch "FFFF";
|
||||||
|
expected = [ 255 255 255 255 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testsHexMatch3 = {
|
||||||
|
expr = self.colors.rgb.hexMatch "0A0B0C0D";
|
||||||
|
expected = [ 10 11 12 13 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testsHexMatch4 = {
|
||||||
|
expr = self.colors.rgb.hexMatch "0A0B0C";
|
||||||
|
expected = [ 10 11 12 ];
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user