mirror of
https://github.com/foo-dogsquared/nixos-config.git
synced 2025-01-31 04:58:01 +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;
|
||||
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
|
||||
# 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
|
||||
|
@ -12,4 +12,5 @@ in
|
||||
math = callLib ./math.nix;
|
||||
trivial = callLib ./trivial;
|
||||
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