packages feed

gruff 0.2 → 0.2.1

raw patch · 4 files changed

+223/−61 lines, 4 files

Files

gruff.cabal view
@@ -1,5 +1,5 @@ Name:                gruff-Version:             0.2+Version:             0.2.1 Synopsis:            fractal explorer GUI using the ruff library Description:     Mandelbrot Set fractal explorer using the ruff library.@@ -10,15 +10,25 @@     with integer-simple instead of the default integer-gmp.  To install     without MPFR support, use @cabal install gruff -f-mpfr@.     .+    This version is a bugfix release.  Changes since gruff-0.2:+    .+      * Supersampling bugs are fixed (no more undocumented quantization+        leading to inconsistent appearance at different zoom levels).+    .+      * Race condition bugs in cache management are fixed (no more+        incompletely rendered images).+    .+      * Internal changes/refactoring to make future additions easier.+    .     Features in this version include:     .       * Interactive fractal browser display (left click to zoom in,         right click to zoom out, middle-click to center).     .-      * Session persistance (stored in @~/.gruff/state.gruff@ - states can+      * Session persistance (stored in @~\/.gruff\/state.gruff@ - states can         also be loaded from and saved to other files).     .-      * Tile cache (by default in @~/.gruff/cache@ - symlink it somewhere+      * Tile cache (by default in @~\/.gruff\/cache@ - symlink it somewhere         with a few GB of space if you plan on exploring a lot).     .       * High-level feature finding using angled internal addresses@@ -37,7 +47,7 @@       * Limited amount of customizable colouring (colours for interior,         border, and exterior points).     .-      * Supersampling for more detailed images (useful range is 1 to 4).+      * Supersampling for more detailed images (useful range is 1 to 16).     .     Future features might include image saving, external ray and feature     information overlays, more feature finding and identification
src/Browser.hs view
@@ -2,7 +2,7 @@  import Prelude hiding (log) import Control.Concurrent-  ( forkIO, MVar, newMVar, takeMVar, putMVar, tryTakeMVar, threadDelay )+  ( forkIO, MVar, newEmptyMVar, newMVar, takeMVar, putMVar, tryTakeMVar, threadDelay ) import Control.Monad (foldM, forever, forM_, replicateM, unless, when) import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef) import Data.List (sortBy)@@ -71,6 +71,9 @@   , combineProg :: Program   , tsheet0 :: TextureObject   , tsheet1 :: TextureObject+  , sheetCount0 :: Int+  , sheetCount1 :: Int+  , sheetCountTarget :: Int   , fbo :: GLuint   , cacheSizeMin :: Int   , cacheSizeMax :: Int@@ -83,8 +86,8 @@   , doClear :: Bool   } -sheetSize :: GruffImage -> Size-sheetSize g = let s = texels . bufferSize . window $ g in Size s s+sheetSize :: GruffImage -> BufferSize+sheetSize g = bufferSize (sheetOffset g) (window g)  zoomPhase :: GruffImage -> Double zoomPhase = delta . location@@ -92,9 +95,23 @@ rotationA :: GruffImage -> Double rotationA = orient . viewport +sheetBlend :: GruffImage -> (Int, Double)+sheetBlend g =+  let z = zoomPhase g+      s = supersamples (window g)+      b = logBase 4 (4 ** z * s)+      h = floor b+      d = fromIntegral h - b+  in  (h, 1 - (1 - 4**d) / (4**(d+1) - 4**d))++sheetOffset :: GruffImage -> Int+sheetOffset = fst . sheetBlend++blendFactor :: GruffImage -> Double+blendFactor = snd . sheetBlend+ iDisplay :: IORef GruffImage -> IO () iDisplay iR = do-  prune iR   s0 <- readIORef iR   mtls <- tryTakeMVar (queue s0)   case mtls of@@ -116,11 +133,11 @@   when c $ do     clearSheet s False     clearSheet s True-  todo0 <- renderSheet s False-  todo1 <- renderSheet s True-  atomicModifyIORef iR $ \s' -> (s'{ viewQuads = (todo0, todo1) }, ())-  let complete = null todo0 && null todo1-      w = width (window s)+    atomicModifyIORef iR $ \s' -> (s'{ sheetCount0 = 0, sheetCount1 = 0 }, ())+  (todo0, upped0) <- renderSheet s False+  (todo1, upped1) <- renderSheet s True+  atomicModifyIORef iR $ \s' -> (s'{ viewQuads = (todo0, todo1), sheetCount0 = sheetCount0 s' + upped0 , sheetCount1 = sheetCount1 s' + upped1 }, ())+  let w = width (window s)       h = height (window s)   GL.viewport $=     (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))@@ -134,10 +151,7 @@   lblend  <- get $ uniformLocation (combineProg s) "blend"   uniform lsheet0 $= TexCoord1 (0 :: GLint)   uniform lsheet1 $= TexCoord1 (1 :: GLint)-  uniform lblend $= TexCoord1 (realToFrac (-    let p = negate $ zoomPhase s-        q = 4 -- realToFrac $ supersamples (window s) -- FIXME figure out correct blend-    in  1 - (1 - q**p) / (q**(p + 1) - q**p) ) :: GLfloat)+  uniform lblend $= TexCoord1 (realToFrac (blendFactor s) :: GLfloat)   activeTexture $= TextureUnit 0   textureBinding Texture2D $= Just (tsheet0 s)   activeTexture $= TextureUnit 1@@ -155,7 +169,7 @@       v :: Int -> Int -> IO ()       v x y = vertex $ Vertex2         (fromIntegral x :: GLdouble) (fromIntegral y :: GLdouble)-      k = 0.25 * realToFrac (0.5 ** zoomPhase s) :: GLdouble+      k = 0.125 * realToFrac (0.5 ** zoomPhase s) :: GLdouble   renderPrimitive Quads $ do     t (-1) 1 >> v 0 h     t (-1) (-1) >> v 0 0@@ -165,17 +179,21 @@   activeTexture $= TextureUnit 0   textureBinding Texture2D $= Nothing   currentProgram $= Nothing-  when complete $ case doneCallback s of-    Nothing -> return ()-    Just act -> do-      atomicModifyIORef iR $ \s' -> (s'{ doneCallback = Nothing, abortCallback = Nothing }, ())-      act+  s'' <- readIORef iR+  when (sheetCount0 s'' + sheetCount1 s'' >= sheetCountTarget s'') $ do+    case doneCallback s of+      Nothing -> return ()+      Just act -> do+        atomicModifyIORef iR $ \s' -> (s'{ doneCallback = Nothing, abortCallback = Nothing }, ())+        act+  prune iR   err <- glGetError   when (err /= 0) $ print ("error: " ++ show err)  clearSheet :: GruffImage -> Bool -> IO () clearSheet s b = do-  let Size tw' th' = sheetSize s+  let tw' = texels $ sheetSize s+      th' = tw'       tw = if b then tw' * 2 else tw'       th = if b then th' * 2 else th'       tsheet = (if b then tsheet1 else tsheet0) s@@ -190,14 +208,15 @@   glGenerateMipmap gl_TEXTURE_2D   textureBinding Texture2D $= Nothing -renderSheet :: GruffImage -> Bool -> IO [(Complex Int, Quad)]+renderSheet :: GruffImage -> Bool -> IO ([(Complex Int, Quad)], Int) renderSheet s b = do-  let Size tw' th' = sheetSize s+  let tw' = texels $ sheetSize s+      th' = tw'       tw = if b then tw' * 2 else tw'       th = if b then th' * 2 else th'       tsheet = (if b then tsheet1 else tsheet0) s       vquads = (if b then snd else fst) (viewQuads s)-  if null vquads then return [] else do+  if null vquads then return ([], 0) else do     bindFBO (fbo s) tsheet     GL.viewport $=       (GL.Position 0 0, GL.Size (fromIntegral tw) (fromIntegral th))@@ -220,11 +239,12 @@     todo <- foldM (\a t -> do a' <- drawQuad (tiles s) t ; return (a' ++ a)) [] vquads     currentProgram $= Nothing     unbindFBO-    when (length todo < length vquads) $ do+    let upped = length vquads - length todo+    when (upped > 0) $ do       textureBinding Texture2D $= Just tsheet       glGenerateMipmap gl_TEXTURE_2D       textureBinding Texture2D $= Nothing-    return todo+    return (todo, upped)  drawQuad :: Map Quad TextureObject3 -> (Complex Int, Quad) -> IO [(Complex Int, Quad)] drawQuad m ijq@(i :+ j, q) = case q `M.lookup` m of@@ -331,7 +351,7 @@       , " --> ", show (cacheSizeMin s0)       ]     bad <- atomicModifyIORef sR $ \s -> -      let Just q0 = originQuad (location s) (bufferSize (window s))+      let Just q0 = originQuad (location s) (sheetSize s)           score = quadDistance q0           o = comparing (score . fst)           (good, bad)@@ -345,17 +365,17 @@   s' <- readIORef sR   log s' Debug $ "updateCallback "   todo' <- atomicModifyIORef sR $ \s ->-    let vq@(qs0, qs1) = fromMaybe ([],[]) $ visibleQuads (window s) (viewport s) (location s)+    let vq@(qs0, qs1) = fromMaybe ([],[]) $ visibleQuads (window s) (viewport s) (location s) (sheetOffset s)         qs = qs0 ++ qs1         todo = S.filter (`M.notMember` tiles s) (S.fromList (map snd qs))-    in  (s{ viewQuads = vq }, todo)+    in  (s{ viewQuads = vq, sheetCountTarget = length qs }, todo)   -- cancel in-progress jobs that aren't still needed   _ <- takeMVar (jobs s')   p <- atomicModifyIORef sR $ \s -> (s{ progress = M.filter (`S.member` todo') (progress s) }, progress s)   mapM_ (`poke` 1) . filter (\w -> case M.lookup w p of Nothing -> False ; Just q -> q `S.notMember` todo') . workers $ s'   -- set new jobs   s <- readIORef sR-  let Just q0 = originQuad (location s) (bufferSize (window s))+  let Just q0 = originQuad (location s) (sheetSize s)       score = quadDistance q0   putMVar (jobs s') . sortBy (comparing score) . S.toList $ todo' \\ S.fromList (M.elems p) @@ -426,20 +446,27 @@  data Browser = Browser   { browserWindow :: GTK.Window+  , browserGL :: GLUTGtk   , browserRender :: Image -> IO () -> IO () -> IO ()   , browserAbort  :: IO ()   , browserSaveImage :: FilePath -> IO ()   , browserSetExitCallback :: IO () -> IO ()   , browserSetReshapeCallback :: (Int -> Int -> IO ()) -> IO ()   , browserSetMouseCallback :: (Complex R -> Double -> IO ()) -> IO ()-  , browserResize :: Int -> Int -> Double -> IO ()+--  , browserResize :: Int -> Int -> Double -> IO ()   }  browserRenders :: Browser -> [(Image, FilePath)] -> IO () browserRenders _ [] = print "done"-browserRenders b ((i, f):ifs) = browserRender b i-  (browserSaveImage b f >> browserRenders b ifs)-  (print "aborted")+browserRenders b ((i, f):ifs) = do+  result <- newEmptyMVar+  postGUISync $ do+          browserRender b i+            (browserSaveImage b f >> putMVar result True)+            (putMVar result False)+          postRedisplay (browserGL b)+  r <- takeMVar result+  if r then browserRenders b ifs else print "aborted"  browserNew :: GLUTGtk -> Pixbuf -> Logger -> FilePath -> IO Browser browserNew gl' icon lg cacheDir' = do@@ -484,6 +511,9 @@     , combineProg = error "combineProg"     , tsheet0 = TextureObject 0     , tsheet1 = TextureObject 0+    , sheetCount0 = 0+    , sheetCount1 = 0+    , sheetCountTarget = maxBound     , cacheSizeMin = 1000     , cacheSizeMax = 1500     , doClear = False@@ -495,7 +525,11 @@         i <- readIORef iR         putJobs iR []         mapM_ (`poke` 1) . workers $ i-        act <- atomicModifyIORef iR $ \s -> (s{ progress = M.empty, doneCallback = Nothing, abortCallback = Nothing }, abortCallback s)+        act <- atomicModifyIORef iR $ \s -> (s+          { progress = M.empty+          , doneCallback = Nothing+          , abortCallback = Nothing+          }, abortCallback s)         fromMaybe (return ()) act       atExit = do         i <- readIORef iR@@ -503,13 +537,22 @@           Nothing -> return ()           Just act -> act       browserRender' img done aborted = do+        s <- readIORef iR+        let wr = width  (imageWindow img)+            hr = height (imageWindow img)+        unless (width (window s) == wr && height (window s) == hr) $ do+          windowResize iw wr hr+        atomicModifyIORef iR $ \s' -> (s'+          { window = imageWindow img+          , viewport = imageViewport img+          , location = imageLocation img+          , colours = imageColours img+          }, ())+        s' <- readIORef iR+        unless (sheetSize s == sheetSize s') (reallocateBuffers iR)         atomicModifyIORef iR $ \i -> (i           { doneCallback = Just done           , abortCallback = Just aborted-          , location = imageLocation img-          , viewport = imageViewport img-          , window = imageWindow img-          , colours = imageColours img           , doClear = True           }, ())         update iR@@ -522,19 +565,20 @@         ( i{ Browser.reshapeCallback = Just act }, () )       browserSetMouseCallback' act = atomicModifyIORef iR $ \i ->         ( i{ mouseCallback = Just act }, () )-      browserResize' w h s = windowResize iw w h >> iReshape iR (Just s) (Size w h)+--      browserResize' w h s = windowResize iw w h >> iReshape iR (Just s) (Size w h)   keyboardMouseCallback gl' $= iMouse iR   _ <- timeoutAdd (timer iR >> return True) msPerFrame   _ <- iw `onDestroy` atExit   return Browser     { browserWindow = iw+    , browserGL = gl'     , browserRender = browserRender'     , browserAbort = browserAbort'     , browserSaveImage = browserSaveImage'     , browserSetExitCallback = browserSetExitCallback'     , browserSetReshapeCallback = browserSetReshapeCallback'     , browserSetMouseCallback = browserSetMouseCallback'-    , browserResize = browserResize'+--    , browserResize = browserResize'     }  fromColours :: Colours -> (Color3 GLfloat, Color3 GLfloat, Color3 GLfloat)@@ -572,13 +616,17 @@     , combineProg = combineProg'     , tsheet0 = tsheet0'     , tsheet1 = tsheet1'+    , sheetCount0 = 0+    , sheetCount1 = 0+    , sheetCountTarget = maxBound     }, ())   reallocateBuffers iR  reallocateBuffers :: IORef GruffImage -> IO () reallocateBuffers iR = do   s <- readIORef iR-  let Size tw' th' = sheetSize s+  let tw' = texels $ sheetSize s+      th' = tw'       ts = [tsheet0 s, tsheet1 s]   forM_ (ts `zip` [(tw', th'), (tw' * 2, th' * 2)]) $ \(t, (tw, th)) -> do     texture Texture2D $= Enabled@@ -591,6 +639,7 @@     textureWrapMode Texture2D T $= (Repeated, ClampToEdge)     textureBinding Texture2D $= Nothing     texture Texture2D $= Disabled+  atomicModifyIORef iR $ \s' -> (s'{ sheetCount0 = 0, sheetCount1 = 0, sheetCountTarget = maxBound }, ())  newFBO :: IO GLuint newFBO = alloca $ \p -> glGenFramebuffers 1 p >> peek p
src/View.hs view
@@ -51,8 +51,10 @@ diagonal2 :: Window -> Int diagonal2 w = width w * width w + height w * height w +{- resolution :: Window -> Double-resolution w = supersamples w * fromIntegral (windowSize w)+resolution w = sqrt $ supersamples w * fromIntegral (diagonal2 w)+-}  pixelLocation :: Window -> Viewport -> Location -> Double -> Double -> Complex Rational pixelLocation w v l x y =@@ -71,7 +73,7 @@       si = sin a       x2 =  co * x1 + si * y1       y2 = -si * x1 + co * y1-      r = 2 * radius l+      r = radius l   in  (r * x2) :+ (r * y2)  data BufferSize = BufferSize@@ -79,11 +81,11 @@   }   deriving (Read, Show, Eq) -bufferSize :: Window -> BufferSize-bufferSize w = BufferSize{ texels = max tileSize . roundUp2 . ceiling . resolution $ w }+bufferSize :: Int -> Window -> BufferSize+bufferSize o w = BufferSize{ texels = roundUp2 . ceiling . ((2::Double) ^^ o *) . sqrt . fromIntegral . diagonal2 $ w }  roundUp2 :: Int -> Int -- fails for too small and too large inputs-roundUp2 x = last . takeWhile (x >) . iterate (2 *) $ 1+roundUp2 x = head . dropWhile (x >=) . iterate (2 *) $ 1  level :: Location -> Int level = floor . negate . logBase 2 . radius@@ -148,13 +150,13 @@       , (i1 :+ j1, SouthEast `child` q)       ] -visibleQuads :: Window -> Viewport -> Location -> Maybe ([(Complex Int, Quad)], [(Complex Int, Quad)])-visibleQuads w v l = do-  let b = bufferSize w+visibleQuads :: Window -> Viewport -> Location -> Int -> Maybe ([(Complex Int, Quad)], [(Complex Int, Quad)])+visibleQuads w v l o = do+  let b = bufferSize o w       a = orient v       co = cos a       si = sin a-      k = 0.5 ** delta l * supersamples w+      k = 0.5 ** (delta l - fromIntegral o)       x1 = k * fromIntegral (width  w)       y1 = k * fromIntegral (height w)       x0 = - x1
src/gruff.hs view
@@ -2,9 +2,10 @@ module Main (main) where  import Prelude hiding (catch, log)+--import Control.Concurrent (forkIO) import Control.Monad (forM_, liftM3, when) import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef, writeIORef)-import Data.Maybe (isJust, fromMaybe)+import Data.Maybe (isJust, fromMaybe) --  , mapMaybe) import Graphics.UI.Gtk hiding (get, Region, Size, Window, Viewport) import qualified Graphics.UI.Gtk as GTK import Graphics.UI.Gtk.OpenGL (initGL)@@ -16,9 +17,9 @@  import Paths_gruff (getDataFileName) import Number (R)-import Browser (Browser(..), browserNew)+import Browser (Browser(..), browserNew) --  , browserRenders) import MuAtom (MuAtom(..), MuProgress(..), muFromAddress, MuProgress'(..), muToAddress, MuProgress''(..), muLocate)-import View (Image(..), Location(..), Viewport(..), Window(..), Colours(..), Colour(..), defWindow, defViewport)+import View (Image(..), Location(..), Viewport(..), Window(..), Colours(..), Colour(..), defWindow, defViewport) --  , defColours) import GLUTGtk (glut, Size(Size), postRedisplay) import Logger (logger, LogLevel(Debug)) import qualified Logger as Log@@ -206,6 +207,7 @@       entI e a = do         _ <- e g0 `onEntryActivate` (entryGetText (e g0) >>= wrapE g0 gR a >> upI)         return ()+{-       entI' e a = do         _ <- e g0 `onEntryActivate` (do           entryGetText (e g0) >>= wrapE g0 gR a@@ -213,6 +215,7 @@           browserResize browser (width (gWindow g)) (height (gWindow g)) (supersamples (gWindow g))           upI)         return ()+-}       colI cbi cbb cbe a = forM_ [cbi, cbb, cbe] $ \c -> do         _ <- c g0 `onColorSet` (do           ci <- colorButtonGetColor (cbi g0)@@ -260,7 +263,7 @@           , () )         g <- readIORef gR         uEverything g0 g-        upI+--        upI   butI bHome aHome   butI bLoad aLoad   but  bSave aSave@@ -273,9 +276,14 @@   entME eImagM eImagE aImag   entME eSizeM eSizeE aSize   entI eRota aRota+  entI eWidth aWidth+  entI eHeight aHeight+  entI eSamples aSamples+{-   entI' eWidth aWidth   entI' eHeight aHeight   entI' eSamples aSamples+-}   colI cInterior cBorder cExterior aColours   let aExit' = (exit (Log.log lg) stateFile =<< readIORef gR)   browserSetExitCallback browser aExit'@@ -288,6 +296,7 @@   refreshGUI g0 g   widgetShowAll iw   widgetShowAll ww+--  _ <- forkIO $ script browser   mainGUI  data Gruff = Gruff@@ -317,7 +326,7 @@   { gAddress    = parseAngledInternalAddress "1"   , gReal       = Just 0   , gImag       = Just 0-  , gSize       = Just 1+  , gSize       = Just 2   , gRota       = Just 0   , gColours    = (red, black, white)   , gWindow     = defWindow@@ -421,7 +430,7 @@         MuSuccess'' mu -> do           let g' = g{ gReal = Just . realPart . muNucleus $ mu                     , gImag = Just . imagPart . muNucleus $ mu-                    , gSize = Just . (* 4) . muSize $ mu+                    , gSize = Just . (* 16) . muSize $ mu                     , gRota = Just . subtract (pi/2) . muOrient $ mu                     }           progress "Found!"@@ -478,7 +487,7 @@           MuSuccess mu     -> do             let g' = g{ gReal = Just . realPart . muNucleus $ mu                       , gImag = Just . imagPart . muNucleus $ mu-                      , gSize = Just . (* 4) . muSize $ mu+                      , gSize = Just . (* 16) . muSize $ mu                       , gRota = Just . subtract (pi/2) . muOrient $ mu                       }             progress "Done!"@@ -556,3 +565,95 @@  fromColor :: Color -> Colour fromColor (Color r g b) = Colour (fromIntegral r / 65535) (fromIntegral g / 65535) (fromIntegral b / 65535)++{-+strings :: [String]+strings =  [ "1 1/3 " ++ (unwords . map show . scanl (+) 3) (replicate m 1 ++ replicate (n - m) 2) | m <- [0 .. n - 1] ]+        ++ [ "1 1/3 " ++ (unwords . map show . scanl (+) 3) (replicate m 2 ++ replicate (n - m) 1) | m <- [0 .. n - 1] ]+  where n = 21 -- 15++script :: Browser -> IO ()+script b = browserRenders b images'++filename :: String -> Int -> String+filename s n = (reverse . take 4 . (++ "0000") . reverse . show) n ++ "__" ++ map filechar s ++ ".ppm"+  where filechar ' ' = '_'+        filechar '/' = '-'+        filechar  c  =  c++findMu :: AngledInternalAddress -> Maybe MuAtom+findMu a = case last (muFromAddress a) of+  MuSuccess m -> Just m+  _ -> Nothing++scene :: (String, Int) -> Maybe (View.Image, String)+scene (s, n) = do+  a <- parseAngledInternalAddress s+  m <- findMu a+  let cx :+ cy = muNucleus m+      f = filename s n+      i = Image+            { imageLocation = Location{ center = toRational cx :+ toRational cy, radius = muSize m * 8 } -- 6+            , imageViewport = viewportDVD{ orient = muOrient m - pi / 2 }+            , imageWindow = windowDVD+            , imageColours = defColours+            }+  return (i, f)++windowDVD :: View.Window+windowDVD = Window{ width = 1080, height = 576, supersamples = 2 }++viewportDVD :: View.Viewport+viewportDVD = Viewport{ aspect = 1080/576, orient = 0 }++images :: [(View.Image, String)]+images = mapMaybe scene (score `zip` [0..])++kick, snare :: Int -> [String]+kick n = [ "1 2 " ++ (unwords . map show . take m . scanl (+) (3 :: Int) . repeat) 1 | m <- [n, n - 1 .. 1] ]+snare n = [ "1 2 " ++ (unwords . map show . take (2 * m) . filter (\x -> x `mod` 3 /= 0)) [(3 :: Int) ..] | m <- [n, n - 1 .. 1] ]++score :: [String]+score = concat+  [ kick 21+  , snare 32+  , kick 20+  , snare 11+  , kick 8+  , kick 8+  , kick 5+  , snare 32+  , kick 20+  , snare 11+  ]++images' :: [(View.Image, String)]+images' = {-drop 49 . -} map frame . take 1200 $ [0 ..]++frame :: Int -> (View.Image, String)+frame n = (img, (reverse . take 4 . (++ "0000") . reverse . show) n ++ ".ppm")+  where+    t = fromIntegral n / 25+    r = 4 * 2 ** negate t+    a = c0a * t / 48+    img = Image+      { imageLocation = Location{ center = c0x :+ c0y, radius = r }+      , imageViewport = viewportHD{ orient = a }+      , imageWindow = windowHD+      , imageColours = defColours+      }++windowHD :: View.Window+windowHD = Window{ width = 1920, height = 1080, supersamples = 5 }++viewportHD :: View.Viewport+viewportHD = Viewport{ aspect = 1920/1080, orient = 0 }++c0x, c0y :: Rational+c0x = -1.249654501250026535305564290406342641357245044812024384076696041016544263255410210573644817965421364058466642283047810126900443791060999959661828661014819271180654290498245641835647321878524004347784493127325654028742057010462138345395182421783064045422740334072967042829523926305969086019115635907387177+c0y =  2.62964442060409470886060596680625049122647005543488791701840256388323764840498489578760240122036234473238581692886963706321452429283848754125169118310897168288049653283033547961422702057918968158064636527854942947851448063883186797623535011066501329732840334929404268157442692714476422929470502521447535e-2++c0a :: Double+c0a = -2.651738023427396++-}