module Maurer where import Graphics.Gloss {-WETT-} {- MCCOMMENT A while ago I saw a video about the Maurer Rose by Daniel Shiffman aka Coding Train on YouTube (https://www.youtube.com/watch?v=4uU9lZ-HSqA&t=663s). I got inspired from his video and tried to recreate. Check out https://handwiki.org/wiki/Maurer_rose for a short explanation about the math behind it. How to build: The visualization can be started by typing 'stack ghci' followed by 'Maurer.main'. I also included a gif in the projects root directory, called Maurer.gif. General concept: First we go through the most pretty variants of the Maurer Rose (in my humble opionion). Afterwards we can get to the very nature of rose by watching it evolve over a time, as time passes and parameters are altered. I did not limit the run time, as the patterns become more interesting / weird over time. Link to final version: https://nc.alexanderstephan.xyz/index.php/s/4A2tFec8ZQKDE33 If you guys find this interesting or cool, I am willing to record a more high quality version but XQuartz on Mac is not scaling properly atm, but no time to experiment due to upcoming exams. -} type Variant = (Float, Float) -- all popular parameters for n and d for a simple show case at the beginning variants :: [Variant] variants = [(2,39), (4,31), (5,97), (6,71), (7,19)] main :: IO () main = animate window background drawing where window = InWindow "Maurer" (800,800) (10,10) background = greyN 0.1 drawing time = Pictures [brd, net] where n = if time > 25 then 0.02 * time else fst $ variants !! (truncate $ time / 5) d = if time > 25 then 0.06 * time else snd $ variants !! (truncate $ time / 5) roseNet = buildRoseNet n d roseMark = buildRoseBorder n d colorPoolA = buildColorsA 0.3 colorPoolB = buildColorsB 0.3 net = color (colorPoolA !! ((round time + 10) `mod` 50)) $ Translate 0 (-10) -- Small color shift so we're able to distinguish the shapes more easily $ Rotate (5 * sin time) $ Scale 2.5 2.5 $ Line roseNet -- Small rotation is applied to give a more dynamic feel, especially in slow moving parts brd = color (colorPoolB !! (round time `mod` 50)) $ Translate 0 (-10) $ Rotate (4.5 * sin time) $ Scale 2.5 2.5 $ Line roseMark -- build body of rose buildRoseNet :: Float -> Float -> [(Float, Float)] buildRoseNet n d = [rosePoint t n d | t <- [0..359]] where rosePoint :: Int -> Float -> Float -> (Float, Float) rosePoint t n d = (x,y) where k = fromIntegral t * d r = 150.0 * sin (degreeToRadians (n * k)) k' = degreeToRadians k r' = degreeToRadians r x = r * cos k' y = r * sin k' -- build outline of the rose buildRoseBorder :: Float -> Float -> [(Float, Float)] buildRoseBorder n d = [rosePoint t n d | t <- [0..359]] where rosePoint :: Int -> Float -> Float -> (Float, Float) rosePoint t n d = (x,y) where k = fromIntegral t r = 150.0 * sin (degreeToRadians (n * k)) k' = degreeToRadians k r' = degreeToRadians r x = r * cos k' y = r * sin k' -- calculations involve degrees, so we need a conversion for the sin function degreeToRadians :: Float -> Float degreeToRadians degree = degree * pi / 180 -- generate a smooth color gradient -- reference: https://krazydad.com/tutorials/makecolors.php (strong recommendation) phase1 :: Float phase1 = 0.0 phase2 :: Float phase2 = 2.0 phase3 :: Float phase3 = 4.0 buildColorsA :: Float -> [Color] buildColorsA freq = [makeColor 0.7 ((sin (freq * i + phase1) * 55 + 200) / 255) ((sin (freq * i + phase2) * 55 + 200) / 255) ((sin (freq * i + phase3) * 55 + 200) / 255) | i <- [1..50]] buildColorsB :: Float -> [Color] buildColorsB freq = [makeColor 1.0 ((sin (freq * i + phase1) * 55 + 200) / 255) ((sin (freq * i + phase2) * 55 + 200) / 255) ((sin (freq * i + phase3) * 55 + 200) / 255) | i <- [1..50]] {-TTEW-}