module Graphics.Proc.Lib.Math.Random( randomSeed, random, random2, randomP2, randomCol, randomCola, -- * Perlin noise -- | Returns the Perlin noise value at specified coordinates. Perlin noise is a random sequence generator producing a more natural, harmonic succession of numbers than that of the standard random() function. It was developed by Ken Perlin in the 1980s and has been used in graphical applications to generate procedural textures, shapes, terrains, and other seemingly organic forms. -- -- processing docs: NoiseDetail(..), noiseDetail, noiseOctaves, noiseSeed, noise1, noise2, noise3, randomGaussian ) where import qualified Control.Arrow as Arr(first, second) import qualified Control.Monad.Trans.State.Strict as S import GHC.Float import qualified System.Random as S import System.Random hiding (random) import qualified Numeric.Noise.Perlin as Perlin import qualified Numeric.Noise as Perlin import Graphics.Proc.Core import Graphics.Proc.Lib.Environment onRandom :: (Maybe StdGen -> IO (a, Maybe StdGen)) -> Pio a onRandom f = do gen <- getRandomGen (res, gen1) <- liftIO $ f gen putRandomGen gen1 return res onNoise :: (NoiseDetail -> Maybe Perlin.Seed -> IO (a, Maybe Perlin.Seed)) -> Pio a onNoise f = do noiseDetail <- getNoiseDetail gen <- getNoiseGen (res, gen1) <- liftIO $ f noiseDetail gen putNoiseGen gen1 return res -- | Sets the seed value for random(). By default, random() produces different results each time the program is run. Set the seed parameter to a constant to return the same pseudo-random numbers each time the software is run. -- -- processing docs: randomSeed :: Int -> Pio () randomSeed n = onRandom $ const $ return ((), Just (mkStdGen n)) -- | Generates random numbers. Each time the random() function is called, it returns an unexpected value within the specified range. If only one parameter is passed to the function, it will return a float between zero and the value of the high parameter. For example, random(5) returns values between 0 and 5 (starting at zero, and up to, but not including, 5). -- -- processing docs: random :: Float -> Pio Float random maxVal= random2 (0, maxVal) -- | Genrates random numbers within the given range. random2 :: (Float, Float) -> Pio Float random2 (minVal, maxVal) = onRandom $ \mg -> case mg of Just g -> return $ Arr.second Just $ randomR (minVal, maxVal) g Nothing -> do res <- randomRIO (minVal, maxVal) return (res, Nothing) -- | Creates random point within the ranges of the size of the screen. randomP2 :: Pio P2 randomP2 = do x <- random =<< winWidth y <- random =<< winHeight return (x, y) -- | Creates random color. randomCol :: Pio Col randomCol = liftA3 (\r g b -> Col r g b 1) (random 1) (random 1) (random 1) -- | Creates random color with transparency. randomCola :: Pio Col randomCola = liftA4 Col (random 1) (random 1) (random 1) (random 1) where liftA4 f a b c d = f <$> a <*> b <*> c <*> d -- | Returns a float from a random series of numbers having a mean of 0 and standard deviation of 1. Each time the randomGaussian() function is called, it returns a number fitting a Gaussian, or normal, distribution. There is theoretically no minimum or maximum value that randomGaussian() might return. Rather, there is just a very low probability that values far from the mean will be returned; and a higher probability that numbers near the mean will be returned. -- -- processing docs: randomGaussian :: Pio Float randomGaussian = do r1 <- random 1 r2 <- random 1 return $ boxMuller 0 1 (r1, r2) -- | BoxMuller algorythm for creation of Gaussian random values out of two uniform number r1, r2. -- r1, r2 should belong to the interval [0, 1] boxMuller :: Floating a => a -> a -> (a, a) -> a boxMuller mu sigma (r1,r2) = mu + sigma * sqrt (-2 * log r1) * cos (2 * pi * r2) -- | Sets the seed value for noise(). By default, noise() produces different results each time the program is run. Set the seed parameter to a constant to return the same pseudo-random numbers each time the software is run. -- -- processing docs: noiseSeed :: Int -> Pio () noiseSeed n = onNoise $ const $ const $ return ((), Just n) -- | Sets the number of octaves for perlin noise. noiseOctaves :: Int -> Pio () noiseOctaves octaves = putOctaves octaves -- | Adjusts the character and level of detail produced by the Perlin noise function. Similar to harmonics in physics, noise is computed over several octaves. Lower octaves contribute more to the output signal and as such define the overal intensity of the noise, whereas higher octaves create finer-grained details in the noise sequence. -- -- By default, noise is computed over 4 octaves with each octave contributing exactly half than its predecessor, starting at 50% strength for the first octave. This falloff amount can be changed by adding an additional function parameter. For example, a falloff factor of 0.75 means each octave will now have 75% impact (25% less) of the previous lower octave. While any number between 0.0 and 1.0 is valid, note that values greater than 0.5 may result in noise() returning values greater than 1.0. -- -- By changing these parameters, the signal created by the noise() function can be adapted to fit very specific needs and characteristics. -- -- processing docs: noiseDetail :: Int -> Float -> Pio () noiseDetail octaves fallOff = putNoiseDetail $ NoiseDetail octaves fallOff -- | Returns 1D Perlin noise. noise1 :: Float -> Pio Float noise1 x = noise3 (x, 0, 0) -- | Returns 2D Perlin noise. noise2 :: P2 -> Pio Float noise2 (x, y) = noise3 (x, y, 0) -- | Returns 3D Perlin noise. noise3 :: P3 -> Pio Float noise3 p = onNoise $ \details mseed -> case mseed of Just g -> return $ (noiseValue g details p, mseed) Nothing -> do seed <- randomIO return $ (noiseValue seed details p, mseed) where noiseValue seed (NoiseDetail octaves fallOff) p = double2Float $ rescale $ Perlin.noiseValue (Perlin.perlin seed octaves scale persistance) (toDoublePoint p) where scale = 1 persistance = f2d fallOff toDoublePoint (x, y, z) = (f2d x, f2d y, f2d z) -- from [-1,1] to [0,1] rescale x = 0.5 * (x + 1)