Fractaler (empty) → 3
raw patch · 3 files changed
+314/−0 lines, 3 filesdep +FTGLdep +GLFW-bdep +OpenGLRawsetup-changed
Dependencies added: FTGL, GLFW-b, OpenGLRaw, base, parallel, random, time
Files
- Fractaler.cabal +13/−0
- Fractaler.hs +299/−0
- Setup.hs +2/−0
+ Fractaler.cabal view
@@ -0,0 +1,13 @@+name: Fractaler+author: serprex+category: Graphics+version: 3+build-type: Simple+license: MIT+description: Fractal renderer+cabal-version: >= 1.4+executable Fractaler+ build-depends: base<=4.8.1.0, time, random, parallel >= 3, OpenGLRaw, GLFW-b, FTGL+ ghc-options: -fno-warn-tabs -threaded -rtsopts -with-rtsopts=-N+ main-is: Fractaler.hs+
+ Fractaler.hs view
@@ -0,0 +1,299 @@+{-# 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)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain