Add polarity option

This commit is contained in:
Daniel Thwaites 2022-06-30 18:17:44 +01:00
parent f7d6ec8f1d
commit 6a706cba69
4 changed files with 40 additions and 21 deletions

View file

@ -55,9 +55,12 @@ a NixOS module; how to do this is shown in the example above.
{ pkgs, ... }:
{
# A colorscheme will be chosen automatically based on your wallpaper
# A colorscheme will be chosen automatically based on your wallpaper.
stylix.image = ./wallpaper.png;
# Use this option to force a light or dark theme.
stylix.polarity = "light";
# Select your preferred fonts, or use these defaults:
stylix.fonts = {
serif = {

View file

@ -11,10 +11,11 @@ import Text.JSON ( encode )
-- | Run the genetic algorithm to generate a palette from the given image.
selectColours :: (Floating a, Real a)
=> V.Vector (LAB a) -- ^ Colours of the source image
=> String -- ^ Scheme type: "either", "light" or "dark"
-> V.Vector (LAB a) -- ^ Colours of the source image
-> V.Vector (LAB a) -- ^ Generated palette
selectColours image
= snd $ evolve image (EvolutionConfig 1000 100 0.5 150) (mkStdGen 0)
selectColours polarity image
= snd $ evolve (polarity, image) (EvolutionConfig 1000 100 0.5 150) (mkStdGen 0)
-- | Convert a 'DynamicImage' to a simple 'V.Vector' of colours.
unpackImage :: (Num a) => DynamicImage -> V.Vector (RGB a)
@ -30,22 +31,23 @@ loadImage :: String -- ^ Path to the file
-> IO DynamicImage
loadImage input = either error id <$> readImage input
mainProcess :: (String, String) -> IO ()
mainProcess (input, output) = do
mainProcess :: (String, String, String) -> IO ()
mainProcess (polarity, input, output) = do
putStrLn $ "Processing " ++ input
image <- loadImage input
let outputTable = makeOutputTable
$ V.map lab2rgb
$ selectColours
$ selectColours polarity
$ V.map rgb2lab
$ unpackImage image
writeFile output $ encode outputTable
putStrLn $ "Saved to " ++ output
parseArguments :: [String] -> Either String (String, String)
parseArguments [input, output] = Right (input, output)
parseArguments [_] = Left "Please specify an output file"
parseArguments [] = Left "Please specify an image"
parseArguments :: [String] -> Either String (String, String, String)
parseArguments [polarity, input, output] = Right (polarity, input, output)
parseArguments [_, _] = Left "Please specify an output file"
parseArguments [_] = Left "Please specify an image"
parseArguments [] = Left "Please specify a polarity: either, light or dark"
parseArguments _ = Left "Too many arguments"
main :: IO ()

View file

@ -34,12 +34,12 @@ randomFromVector generator vector
= let (index, generator') = randomR (0, V.length vector - 1) generator
in (vector ! index, generator')
instance (Floating a, Real a) => Species (V.Vector (LAB a)) (V.Vector (LAB a)) where
instance (Floating a, Real a) => Species (String, (V.Vector (LAB a))) (V.Vector (LAB a)) where
{- |
Palettes in the initial population are created by randomly
sampling 16 colours from the source image.
-}
generate image = generateColour 16
generate (_, image) = generateColour 16
where generateColour 0 generator = (generator, V.empty)
generateColour n generator
= let (colour, generator') = randomFromVector generator image
@ -51,17 +51,13 @@ instance (Floating a, Real a) => Species (V.Vector (LAB a)) (V.Vector (LAB a)) w
Mutation is done by replacing a random slot in the palette with
a new colour, which is randomly sampled from the source image.
-}
mutate image generator palette
mutate (_, image) generator palette
= let (index, generator') = randomR (0, 15) generator
(colour, generator'') = randomFromVector generator' image
in (generator'', palette // [(index, colour)])
fitness _ palette
= realToFrac $
accentDifference -
-- Either light schemes or dark themes are allowed.
-- We try to converge on whichever theme we are closer to.
min lightScheme darkScheme
fitness (polarity, _) palette
= realToFrac $ accentDifference - scheme
where
-- The accent colours should be as different as possible.
accentDifference = minimum $ do
@ -79,6 +75,12 @@ instance (Floating a, Real a) => Species (V.Vector (LAB a)) (V.Vector (LAB a)) w
-- The accent colours should all have the given lightness.
+ sum (V.map (difference accentValue) $ accent lightnesses)
scheme = case polarity of
"either" -> min lightScheme darkScheme
"light" -> lightScheme
"dark" -> darkScheme
_ -> error ("Invalid polarity: " ++ polarity)
{-
For light themes, the background is bright and the text is dark.
The accent colours are slightly darker.

View file

@ -7,7 +7,7 @@ let
cfg = config.stylix;
paletteJSON = pkgs.runCommand "palette.json" { } ''
${palette-generator}/bin/palette-generator ${cfg.image} $out
${palette-generator}/bin/palette-generator ${cfg.polarity} ${cfg.image} $out
'';
palette = importJSON paletteJSON // {
@ -18,6 +18,18 @@ let
in {
options.stylix = {
polarity = mkOption {
type = types.enum [ "either" "light" "dark" ];
default = "either";
description = ''
Use this option to force a light or dark theme.
By default we will select whichever is ranked better by the genetic
algorithm. This aims to get good contrast between the foreground and
background, as well as some variety in the highlight colours.
'';
};
image = mkOption {
type = types.coercedTo types.package toString types.path;
description = ''