{-# LANGUAGE BangPatterns#-} {-# OPTIONS -fexcess-precision -funbox-strict-fields -feager-blackholing -O2#-} module Main(main) where import Prelude hiding (init) import Graphics.Rendering.OpenGL.Raw.Version21 import Graphics.Rendering.FTGL import Graphics.UI.GLFW import Data.IORef import Data.Complex hiding (magnitude) --Too correct. Faster magnitude in Templates import Foreign.Ptr import Foreign.Marshal.Alloc import Foreign.Marshal.Array import Foreign.Storable import Control.Monad import Control.Parallel.Strategies(parBuffer,rseq,withStrategy) import System.IO(hFlush,stdout) import System.Random(randomRIO,randomIO,randomRs,mkStdGen) import System.Environment(getArgs) import GHC.Float(double2Float) import Templates doUntil :: (a -> Bool) -> IO a -> IO a doUntil p x = do r <- x if p r then return r else doUntil p x rancom :: (Double,Double) -> (Double,Double) -> IO (Complex Double) ranpol :: IO [Complex Double] rancom x y = randomRIO x >>= return . (:+) >>= (randomRIO y >>=) . (return .) ranpol = randomIO >>= return . flip take . zipIt . randomRs (-4,4) . mkStdGen >>= (randomRIO (3,8) >>=) . (return .) where zipIt (x:y:xs) = (x:+y):zipIt xs ranpolmag = ranpol >>= mapM (\(x:+y) -> randomRIO (1,99) >>= (\z-> return (((x/2):+(y/2)),z))) winpr :: Show a => Window -> a -> IO () winpr wnd x = setWindowTitle wnd (show x) >> print x getwinwid :: Window -> IO Double getwinwid wnd = do (w,_) <- getWindowSize wnd return $ fromIntegral w get :: IORef a -> IO a get = readIORef ($=) :: IORef a -> a -> IO () ($=) = writeIORef ($~) :: IORef a -> (a -> a) -> IO () ($~) = modifyIORef main = do setErrorCallback $ Just (\e s -> putStrLn $ unwords [show e, show s]) init (Just wnd) <- createWindow 512 512 "Fractaler" Nothing Nothing makeContextCurrent (Just wnd) glEnable gl_TEXTURE_2D font <- createTextureFont "DejaVuSansMono.ttf" setFontFaceSize font 16 0 gfxtx <- alloca (\x -> glGenTextures 1 x >> peek x) glBindTexture gl_TEXTURE_2D gfxtx glTexParameteri gl_TEXTURE_2D gl_TEXTURE_WRAP_T $ fromIntegral gl_CLAMP_TO_EDGE glTexParameteri gl_TEXTURE_2D gl_TEXTURE_WRAP_S $ fromIntegral gl_CLAMP_TO_EDGE glTexParameteri gl_TEXTURE_2D gl_TEXTURE_MAG_FILTER $ fromIntegral gl_NEAREST glTexParameteri gl_TEXTURE_2D gl_TEXTURE_MIN_FILTER $ fromIntegral gl_NEAREST reshaper wnd 512 512 args <- getArgs rnd <- return $ null args xydrt <- newIORef False xyold <- newIORef (-2,-2,4) xynew <- newIORef (0,0) fdrt <- newIORef True fiva <- newIORef 2 finc <- newIORef 25 func <- newIORef mandel let meop x y = func $= x >> finc $= y in main' wnd xydrt xyold xynew fdrt fiva finc func gfxtx font [ ("Reset", xyold$=(-2,-2,4)), ("Julia", do cm<-if rnd then rancom (-1.5,1.4) (-1.7,1.7) else getPrompt "Coordinate" >>= return . readComp winpr wnd cm meop (julia cm) 100), ("JuliaInMan", do cm<-doUntil ((==(0,0,0)) . mandel 2) $ rancom (-2,1.5) (-2,2) winpr wnd cm meop (julia cm) 100), ("JuliaInManButOut", do cm<-doUntil (\x->mandel 9 x==(0,0,0)&&mandel 999 x/=(0,0,0)) $ rancom (-2,1.5) (-2,2) winpr wnd cm meop (julia cm) 100), ("Mandelbrot",meop mandel 100), ("Multi", do ex<-if rnd then randomRIO (3,8) else getPrompt "Exponent" >>= return . (truncate::Double->Int) . readDoub winpr wnd ex meop (multibrot ex) 25), ("Tricorn",meop tricorn 100), ("Burningship",meop burningship 100), ("Half I",meop nodoub 100), ("Dagger",meop dagger 100), ("Collatz",meop collatz 10), ("XxY",meop yxmandel 100), ("Poly", do pl<-if rnd then ranpol else getPrompt "Coefficients" >>= return . readPoly winpr wnd pl meop (newton (makePolyF pl) (makePolyF $ diffPoly pl)) 5), ("GeneralPoly", do pl<-if rnd then ranpol else getPrompt "Coefficients" >>= return . readPoly cm<-if rnd then randomRIO (-2,2) else getPrompt "Multiplier" >>= return . readDoub winpr wnd $ show cm++show pl meop (newton ((*(cm:+0)) . makePolyF pl) (makePolyF $ diffPoly pl)) 5), ("x5-1",meop (newton (\x->x^5-1) (\x->5*x^4)) 5), ("x5+3x3-x2-1",meop (newton (\x->x^5+3*x^3-x*x-1) (\x->5*x^4+9*x*x+x+x)) 5), ("2x3-2x+2",meop (newton (\x->2*x*x*x-2*x+2) (\x->6*x*x-2)) 5), ("sin3-1",meop (newton (\x->sin x^3-1) (\x->3*cos x*sin x*sin x)) 5), ("asin",meop (newton asin (\x->1/sqrt(1-x*x))) 5), ("",meop (newton ((:+0) . phase) (\x->1/(1+x*x))) 5), ("+",meop (newton ((:+0) . phase) (\x->1+x+x)) 5), ("*",meop (newton ((:+0) . phase) (\x->1+x*x)) 5), ("^",meop (newton ((:+0) . phase) (\x->1+x**x)) 5), ("i",meop (newton ((0:+) . phase) (\x->1/(1+x*x))) 5), ("+i",meop (newton ((0:+) . phase) (\x->1+x+x)) 5), ("*i",meop (newton ((0:+) . phase) (\x->1+x*x)) 5), ("^i",meop (newton ((0:+) . phase) (\x->1+x**x)) 5), ("xx",meop (newton (\x->x**x) (\x->exp(x*log x)*(1+log x))) 5), ("xx-1",meop (newton (\x->x**x-1) (\x->exp(x*log x)*(1+log x))) 5), ("xx+x2-x",meop (newton (\x->x**x+x*x-1) (\x->x**x*(1+log x)+x+x)) 5), ("xx-sin x",meop (newton (\x->x**x-sin x) (\x->x**x*(1+log x)+cos x)) 5), ("x.5-x",meop (newton (\x->sqrt x-x) (\x->2/sqrt x-1)) 5), ("Bulbs",meop (newton (\x->2*(x-1)*(x-(0:+1))) (\x->x-(1:+(-1)))) 5), ("Julia",meop (newton (\x->2*(x-1)*(x-(0:+1))) (\x->4)) 5), ("Sheath",meop (newton (\x->2*(x-1)*(x-(1:+1))) (\x->2*x*x-((-1):+1)*x-1)) 5), ("Tricorn",meop (newton conjugate id) 5), ("1",meop (newton (\(x:+y)->(x*x):+(x-y)) (\(x:+y)->(x+y*x):+(y+x))) 5), ("2",meop (newton (\(x:+y)->(x*x-1):+(x-y-1)) (\(x:+y)->(x+y*x+1):+(y+x))) 5), ("Loops",meop (newton id conjugate) 5), ("Spiral",meop (newton id (\x->phase x:+magnitude x)) 5), ("Hand",meop (newton (\(x:+y)->(x*x-1):+(x-y+1)) (\(x:+y)->(x+y*x+1):+(y+x))) 5), ("Sierpinski's Carpet",meop sicarp 1), ("Sierpinski's Tablecloth",meop sitabl 1), ("Cantor Dust",meop cantor 1), ("Cantor Ecstasy",meop cantox 1), ("Poly", do pl<-if rnd then ranpol else getPrompt "Coefficients" >>= return . readPoly winpr wnd pl meop (complex $ makePolyF pl) 1), ("x",meop (complex id) 1), ("xx",meop (complex (\x->x**x)) 1), ("(x2-1)(x-2-i)2/(x2+2+2i)",meop (complex (\x->(x^2-1)*(x-(2:+(-1)))^2/(x^2+(2:+2)))) 1), ("sin",meop (complex sin) 1), ("sin . cos",meop (complex (sin . cos)) 1), ("metaballs", do bs <- if rnd then ranpolmag else (do pol <- getPrompt "Balls" >>= return . readPoly mag <- getPrompt "Size" >>= return . readDoubs return $ zip pol mag) meop (metaball bs) 80)] where getPrompt x = putStr (x++": ") >> hFlush stdout >> getLine main' :: Window -> IORef Bool -> IORef (Double,Double,Double) -> IORef (Double,Double) -> IORef Bool -> IORef Int -> IORef Int -> IORef FractalCb -> GLuint -> Font -> [(String, IO ())] -> IO () main' wnd xydrt xyold xynew fdrt fiva finc func gfxtx font menu = do setKeyCallback wnd $ Just $ keyZoom fdrt fiva setWindowSizeCallback wnd $ Just $ reshaper setMouseButtonCallback wnd $ Just $ displayZoom xydrt xyold xynew fdrt finc func menu setScrollCallback wnd $ Just $ \wnd dx dy -> detailZoom fdrt fiva (if dy<0 then (-1) else 1) wnd setWindowCloseCallback wnd $ Just $ (flip setWindowShouldClose) True mainLoop where evalMenu :: [(String, IO ())] -> Int -> Int -> IO () evalMenu ((a,f):as) x y = let (diffx,diffy) = (if y==0 then (196, 496) else (0, -16)) in do renderFont font a Front glTranslatef (fromIntegral diffx) (fromIntegral diffy) 0 (if null as then return () else evalMenu as (x+diffx) (y+diffy)) mainLoop :: IO () mainLoop = do get fdrt >>= (flip when $ do fdrt $= False displayMap xyold fiva finc func wnd) glBegin gl_QUADS glTexCoord2f 0 0 glVertex2f (-1) (-1) glTexCoord2f 0 1 glVertex2f 1 (-1) glTexCoord2f 1 1 glVertex2f 1 1 glTexCoord2f 1 0 glVertex2f (-1) 1 glEnd getMouseButton wnd MouseButton'2 >>= (flip (when.(MouseButtonState'Pressed==)) $ do glColor3f 1 0 0 glPushMatrix glOrtho 0 511 0 511 (-1) 1 glTranslatef 0 496 0 evalMenu menu 0 496 glPopMatrix glColor3f 1 1 1 glBindTexture gl_TEXTURE_2D gfxtx) swapBuffers wnd glClear gl_COLOR_BUFFER_BIT waitEvents windowShouldClose wnd >>= (flip unless) mainLoop detailZoom :: IORef Bool -> IORef Int -> Int -> Window -> IO () detailZoom fdrt fiva dir wnd = do fiva $~ (max 0 . (+) dir) get fiva >>= winpr wnd fdrt $= True reshaper :: WindowSizeCallback reshaper wnd xx yy = let x=min xx yy in do glViewport 0 0 (toEnum x) (toEnum x) zoomAdjust :: IORef (Double,Double,Double) -> Window -> (Double,Double) -> IO (Double,Double) zoomAdjust xyold wnd (x,y) = do w <- getwinwid wnd (a,b,c) <- get xyold return $! (a+x*(c/w),b+c-y*(c/w)) keyZoom :: IORef Bool -> IORef Int -> KeyCallback keyZoom fdrt fiva wnd (Key'Up) _ KeyState'Pressed _ = detailZoom fdrt fiva 1 wnd keyZoom fdrt fiva wnd (Key'Down) _ KeyState'Pressed _ = detailZoom fdrt fiva (-1) wnd keyZoom _ _ wnd (Key'Escape) _ KeyState'Pressed _ = setWindowShouldClose wnd True keyZoom _ _ _ _ _ _ _ = do return () displayZoom :: IORef Bool -> IORef (Double,Double,Double) -> IORef (Double,Double) -> IORef Bool -> IORef Int -> IORef FractalCb -> [(String, IO ())] -> MouseButtonCallback displayZoom xydrt xyold xynew _ _ _ _ wnd MouseButton'1 MouseButtonState'Pressed _ = do xy <- getCursorPos wnd xydrt $= True >> zoomAdjust xyold wnd xy >>= (xynew$=) displayZoom xydrt xyold xynew fdrt _ _ _ wnd MouseButton'1 MouseButtonState'Released _ = do xy <- getCursorPos wnd xyd <- get xydrt when xyd $ do xydrt $= False (xn,yn) <- get xynew (x,y) <- zoomAdjust xyold wnd xy xyn <- if x==xn && y==yn then get xyold >>= (\(_,_,c)->return (x-c,y-c,c*2)) else return (min x xn,min y yn,max (abs $ x-xn) (abs $ y-yn)) xyold $= xyn winpr wnd xyn fdrt $= True displayZoom _ xyold _ fdrt finc func _ wnd MouseButton'3 MouseButtonState'Pressed _ = do xy <- getCursorPos wnd (x,y) <- zoomAdjust xyold wnd xy func $= julia (x:+y) finc $= 100 fdrt $= True displayZoom _ _ _ fdrt _ _ menu wnd MouseButton'2 MouseButtonState'Released _ = do (x,y) <- getCursorPos wnd mid <- return $ truncate $ (y/16)+(if x<196 then 0 else 32) print mid when (mid < length menu) $ do snd $ menu !! mid fdrt $= True displayZoom xydrt _ _ _ _ _ _ _ _ _ _ = xydrt $= False pts :: (Double,Double,Double) -> Double -> [Complex Double] pts (x1,y1,c) w = [(x1+(c/w)*x):+(y1+(c/w)*y)|x<-[0..w],y<-[0..w]] displayMap :: IORef (Double,Double,Double) -> IORef Int -> IORef Int -> IORef FractalCb -> Window -> IO () displayMap xyold fiva finc func wnd = do w <- getwinwid wnd >>= return . (subtract 1) tw <- return $ truncate w xy <- get xyold func <- get func inc <- get finc vc <- get fiva >>= return . (inc*) (Just t) <- getTime colors <- return $ concat $ map (\(r,g,b) -> map double2Float [r,g,b]) $ withStrategy (parBuffer 512 rseq) . map (func vc) $ pts xy w withArray colors $ glTexImage2D gl_TEXTURE_2D 0 (fromIntegral gl_RGB) (tw+1) (tw+1) 0 gl_RGB gl_FLOAT getTime >>= (\(Just t2) -> (print . subtract t) t2) readDoub :: String -> Double readComp :: String -> Complex Double readMulti :: (String -> a) -> String -> [a] readDoubs :: String -> [Double] readPoly :: String -> [Complex Double] diffPoly :: [Complex Double] -> [Complex Double] makePolyF :: [Complex Double] -> Complex Double -> Complex Double readDoub [] = 0 readDoub (' ':x) = readDoub x readDoub ('-':x) = negate $ readDoub x readDoub x = if all (flip elem "0123456789. ") x && sum(map (fromEnum . (=='.')) x)<2 then read ('0':x) else 0 readComp x = (\(x,y)->(if x==[] then 0 else readDoub x):+if y==[] then 0 else readDoub $ tail y) $ break (=='+') x readMulti f x = readM $ break (==',') x where readM (x,[]) = [f x] readM (x,_:xs) = f x:(readM $ break (==',') xs) readDoubs = readMulti readDoub readPoly = readMulti readComp diffPoly [] = [] diffPoly [x] = [] diffPoly (_:x:xs) = x:zipWith (\(x:+y) z->(x*z):+(y*z)) xs [2..] makePolyF [] y = 0 makePolyF (xh:x) y = xh+(sum $ zipWith (*) x $ iterate (y*) y)