module Progress (Progress(..), progressNew, Cont) where import Control.Monad (forM_, when) import Graphics.UI.Gtk (Pixbuf) import Fractal.RUFF.Mandelbrot.Address import Fractal.RUFF.Mandelbrot.Atom import Fractal.RUFF.Types.Complex import Number (R) import StatusDialog -- | A continuation gets passed the result. type Cont a = (Maybe a -> IO ()) -> IO () -- | Cancellable actions with status updates. data Progress = Progress { progressLocate :: Complex Rational -> Double -> Cont (MuAtom Rational) , progressAtom :: AngledInternalAddress -> Cont (MuAtom Rational) , progressAddress :: MuAtom Rational -> Cont AngledInternalAddress } -- | Create a new Progress with a status dialog. progressNew :: Pixbuf -> IO Progress progressNew icon = do -- FIXME this is a horrible hack to avoid race conditions when re-using the same status dialog sd1 <- statusDialogNew icon sd2 <- statusDialogNew icon sd3 <- statusDialogNew icon return Progress { progressLocate = progressLocate' sd1 , progressAtom = progressAtom' sd2 , progressAddress = progressAddress' sd3 } -- | Much like Fractal.RUFF.Mandelbrot.Atom.locate_ progressLocate' :: StatusDialog -> Complex Rational -> Double -> Cont (MuAtom Rational) progressLocate' sd (re :+ im) r next = do statusDialog sd "gruff status" $ \progress' -> do let c = fromRational re :+ fromRational im :: Complex R forM_ (locate c r) $ \mp -> case mp of LocateScanTodo -> progress' "Scanning for period..." LocateScan -> progress' "Scanning for period..." LocateScanDone p -> progress'$"Scanning for period... " ++ show p LocateNucleusTodo -> progress' "Computing nucleus..." LocateNucleus i -> when (i `mod` 20 == 0) . progress'$"Computing nucleus... " ++ show i LocateNucleusDone _ -> progress' "Computing nucleus... done" LocateBondTodo -> progress' "Computing bond..." LocateBond i -> when (i `mod` 20 == 0) . progress'$"Computing bond... " ++ show i LocateBondDone _ -> progress' "Computing bond... done" LocateSuccess mu -> do progress' "Success!" let (re' :+ im') = muNucleus mu next $ Just mu{ muNucleus = toRational re' :+ toRational im' } LocateFailed -> do progress' "Failed!" next $ Nothing -- | Much like Fractal.RUFF.Mandelbrot.Atom.findAddress_ progressAddress' :: StatusDialog -> MuAtom Rational -> Cont AngledInternalAddress progressAddress' sd mu next = do statusDialog sd "gruff status" $ \progress' -> do let re :+ im = muNucleus mu c = fromRational re :+ fromRational im :: Complex R forM_ (findAddress mu{ muNucleus = c }) $ \mp' -> case mp' of AddressCuspTodo -> progress' "Computing cusp..." AddressCuspDone _ -> progress' "Computing cusp... done" AddressDwellTodo -> progress' "Computing dwell..." AddressDwell i -> when (i `mod` 100 == 0) . progress'$"Computing dwell... " ++ show i AddressDwellDone _ -> progress' "Computing dwell... done" AddressRayOutTodo -> progress' "Tracing rays..." AddressRayOut i -> progress'$"Tracing rays... " ++ show (round $ i * 100 :: Int) ++ "%" AddressRayOutDone _ -> progress' "Tracing rays... done" AddressExternalTodo -> progress' "Computing angle..." AddressExternalDone _ -> progress' "Computing angle... done" AddressAddressTodo -> progress' "Finding address..." AddressSuccess a -> do progress' "Success!" next $ Just a AddressFailed -> do progress' "Failed!" next $ Nothing -- | Much like Fractal.RUFF.Mandelbrot.Atom.findAtom_ progressAtom' :: StatusDialog -> AngledInternalAddress -> Cont (MuAtom Rational) progressAtom' sd addr next = do statusDialog sd "gruff status" $ \progress' -> do forM_ (findAtom addr) $ \mp -> case mp of AtomSplitTodo -> progress' "Splitting address..." AtomSplitDone _ _ -> progress' "Splitting address... done" AtomAnglesTodo -> progress' "Computing angles..." AtomAnglesDone _ _ -> progress' "Computing angles... done" AtomRayTodo -> progress' "Tracing rays..." AtomRay n -> when (n `mod` 20 == 0) . progress'$"Tracing rays... " ++ show n AtomRayDone _ -> progress' "Tracing rays... done" AtomNucleusTodo -> progress' "Computing nucleus..." AtomNucleus n -> when (n `mod` 20 == 0) . progress'$"Computing nucleus... " ++ show n AtomNucleusDone _ -> progress' "Computing nucleus... done" AtomBondTodo -> progress' "Computing bond..." AtomBond n -> when (n `mod` 20 == 0) . progress'$"Computing bond... " ++ show n AtomBondDone _ -> progress' "Computing bond... done" AtomSuccess mu -> do progress' "Success!" let (re' :+ im') = muNucleus mu :: Complex R next $ Just mu{ muNucleus = toRational re' :+ toRational im' } AtomFailed -> do progress' "Failed!" next $ Nothing