packages feed

wholepixels-1.0: src/WholePixels/Random.hs

module WholePixels.Random where

import Relude
import Control.Monad.Random
import WholePixels.Geometry
import WholePixels.Color
import qualified System.Random.Shuffle

disturbedSequence :: MonadRandom m => [Double] -> Double -> m [Double]
disturbedSequence xs amp = do
    dxs <- replicateM (length xs) $ getRandomR (-amp, amp)
    pure $ zipWith (+) xs dxs

filterRandomly :: MonadRandom m => Double -> [a] -> m [a]
filterRandomly probability xs = do
    ps <- replicateM (length xs) $ getRandomR (0, 1)
    pure [x | (x, p) <- zip xs ps, p < probability]

coinToss :: MonadRandom m => m Bool
coinToss = uniform [False, True]

genGrid :: MonadRandom m => Int -> Int -> m a -> m [(Int, Int, a)]
genGrid colCount rowCount genElement =
    genGridWithBoundaries colCount rowCount (const genElement)

genGridWithBoundaries :: MonadRandom m => Int -> Int -> ([Direction] -> m a) -> m [(Int, Int, a)]
genGridWithBoundaries colCount rowCount genElement = do
    let numberGrid = [(x, y) | y <- [0 .. rowCount - 1], x <- [0 .. colCount - 1]]
    forM numberGrid $ \(x, y) -> do
        let dirs = [ R | x == 0] <> [ D | y == 0] <> [ L | x == colCount - 1] <> [ U | y == rowCount - 1]
        (x, y,) <$> genElement dirs

genGrid' :: MonadRandom m => GridSpec -> ([Direction] -> m a) -> m [(Int, Int, a)]
genGrid' (GridSpec gridSpec) genElement = do
    let rowCount = length gridSpec
        colCount = case gridSpec of
            [] -> 0
            (row : _) -> length row
        numberedGrid :: [(Int, Int, CellSpec)]
        numberedGrid = concat $ zipWith
            (\j row -> map (\(i, c) -> (i, j, c)) row)
            [0..rowCount]
            (map
                (zip [0..colCount])
                gridSpec)
    fmap catMaybes . forM numberedGrid $ \(x, y, c) -> do
        let dirs = [ R | x == 0] <> [ D | y == 0] <> [ L | x == colCount - 1] <> [ U | y == rowCount - 1]
        case c of
            N -> pure Nothing
            Y -> (Just . (x, y,)) <$> genElement dirs

probably :: MonadRandom m => Double -> a -> m (Maybe a)
probably probability thing = do
    x <- getRandomR (0, 1)
    pure $
        if x < probability
        then Just thing
        else Nothing

shuffleM :: MonadRandom m => [a] -> m [a]
shuffleM = System.Random.Shuffle.shuffleM

data PaletteStrategy
    = Analogous
    | Complementary
    | SplitComplementary
    | Triangle

genPalette :: MonadRandom m => m Palette
genPalette = do
    strategy <- uniform [Analogous, Complementary, SplitComplementary, Triangle]
    genPaletteWithStrategy strategy

genPaletteWithStrategy :: MonadRandom m => PaletteStrategy -> m Palette
genPaletteWithStrategy strategy = do
    baseHue <- getRandomR (0, 360)
    baseSaturation <- getRandomR (0, 1)
    baseValue <- getRandomR (0, 0.5)
    bgToFgHueDifference <- uniform [180, 0, 30, 120, 240]
    let baseColor = HSV baseHue baseSaturation baseValue
        c = HSV (fixHue $ baseHue + bgToFgHueDifference) 1 1
        colors = case strategy of
            Analogous -> take 4 $ analogous c
            Complementary -> take 3 (analogous c) <> [complementary c]
            SplitComplementary ->
                let (c1, c2) = splitComplementary c
                in [c, c1, c2]
            Triangle ->
                let (c1, c2) = splitTriangle c
                in [c, c1, c2]
    pure $ Palette {..}

genMonochromePaletteForColor :: MonadRandom m => HSV -> m Palette
genMonochromePaletteForColor baseColor@(HSV bh bs bv) = do
    let colors = [HSV bh bs v | v <- [bv + 0.15, bv + 0.2 .. 1]]
    pure $ Palette {..}

genMonochromePalette :: MonadRandom m => Double -> m Palette
genMonochromePalette maxSaturation = do
    hue <- getRandomR (0, 360)
    saturation <- getRandomR (0.0, maxSaturation)
    baseValue <- getRandomR (0, 0.5)
    let baseColor = HSV hue saturation baseValue
        colors = [HSV hue saturation v | v <- [baseValue + 0.15, baseValue + 0.2 .. 1]]
    pure $ Palette {..}

genColor' :: MonadRandom m => Palette -> m HSV
genColor' pal = genColor pal 0.5 1.0

genColor :: MonadRandom m => Palette -> Double -> Double -> m HSV
genColor Palette {..} expected sigma2 = do
    let colorCount = length (baseColor : take 10 colors)
        colorPositions = take colorCount [0.0, (1.0 / fromIntegral colorCount) ..]
        weights = map
            (\p -> toRational $ 1000.0 * exp (- (p - expected) * (p - expected) / sigma2))
            colorPositions
    weighted $ zip (baseColor : colors) weights

genRectSubdivision :: forall m. MonadRandom m => Int -> Rect -> m [Rect]
genRectSubdivision depth r = foldr (>=>) pure (replicate depth step) [r]
    where
    step :: [Rect] -> m [Rect]
    step rs = concat <$> mapM go rs
    go :: Rect -> m [Rect]
    go r' = do
        let hbonusWeight = if rh r' > rw r' then 15 else 0
            vbonusWeight = if rw r' > rh r' then 15 else 0
        f <- weighted
            [ (pure . pure, 3)
            , (pure . hsplit, 1 + hbonusWeight)
            , (pure . hsplitGolden, 3 + hbonusWeight)
            , (pure . hsplitReverseGolden, 3 + hbonusWeight)
            , (pure . vsplit, 1 + vbonusWeight)
            , (pure . vsplitGolden, 3 + vbonusWeight)
            , (pure . vsplitReverseGolden, 3 + vbonusWeight)
            ]
        f r'