packages feed

mandulia 0.4 → 0.5

raw patch · 20 files changed

+506/−324 lines, 20 files

Files

ChangeLog view
@@ -1,6 +1,42 @@-ChangeLog exported from Git by claude@zebimus at 2010-07-12T20:23:57+0100+ChangeLog exported from Git by claude@zebimus at 2010-07-25T03:55:40+0100 - (HEAD, tag: v0.4, origin/master, origin/HEAD, master)+ (HEAD, tag: v0.5, origin/master, origin/HEAD, master)++7b5e311 2010-07-25 03:51:26 +0100 v0.5 neon in O(1) space+69ec357 2010-07-25 03:21:02 +0100 fix annoying disappearing/appearing julias at edge of window+cf6fb48 2010-07-25 02:42:00 +0100 fix aspect ratio to keep julias square+ac1f7be 2010-07-25 02:41:18 +0100 fix displaySize for aspect ratios other than 16:9+05a5e35 2010-07-25 02:23:22 +0100 fix screenshot+099b264 2010-07-25 02:12:40 +0100 bump version number+                                  add note about SSE4 flag+98d09bb 2010-07-25 02:05:04 +0100 update to current code+a94f0e7 2010-07-25 02:04:43 +0100 update to current code+1baef1a 2010-07-25 02:00:51 +0100 fix screenshots/recording comments+9c350a8 2010-07-25 02:00:10 +0100 prune dead code+e59440e 2010-07-25 01:28:20 +0100 merge random and interactive configurations into one main configuration+c874936 2010-07-25 01:26:00 +0100 reduce heap limit as we can run in constant space now+c013ea2 2010-07-25 01:25:28 +0100 recompute tiling on instead of sharing to fix massive memory leak+                                  get statistics for Lua interface+56edf21 2010-07-25 01:23:33 +0100 strictify to avoid memory leak+ebd2a33 2010-07-25 01:22:38 +0100 attempt to fix memory leak by pattern-matching on ()+177423a 2010-07-25 01:21:48 +0100 attempt to store statistics more strictly+b1f0293 2010-07-25 01:21:06 +0100 strict version of modifyIORef+74d8184 2010-07-23 05:56:43 +0100 additional file to distribute+2296642 2010-07-23 05:52:48 +0100 colour using Mandelbrot Set escape time in addition+1631f5b 2010-07-23 05:51:53 +0100 larger field for Julia Set renders+89e15fd 2010-07-23 05:50:13 +0100 improved escapetime to colour function+39aed4f 2010-07-23 05:46:21 +0100 remove unused includes+5f9aa1a 2010-07-23 05:40:33 +0100 background back to black+01bca99 2010-07-23 05:38:22 +0100 larger display size+8086318 2010-07-23 05:37:49 +0100 use filename for reinclusion defence macro+ccaf263 2010-07-23 00:06:25 +0100 ignore binary+bddc637 2010-07-23 00:05:52 +0100 avoid visiting near recent history to make attract mode more interesting+88647a8 2010-07-20 22:59:16 +0100 attract mode improvements: self-centering random walk for zoom level+                                  target border of Mandelbrot set as more interesting+                                  random walk near previous target+320e8ee 2010-07-20 22:57:04 +0100 exterior distance from Mandelbrot Set++ (tag: v0.4)  9d98a8c 2010-07-12 20:17:15 +0100 v0.4 joining the cabal 3a4ccd5 2010-07-12 20:08:50 +0100 fix base dependency to allow successful compilation with ghc-6.10.4
HACKING view
@@ -29,6 +29,6 @@ src/Vector.hs           - linear algebra ((2+1)D homegenous coordinates)  config/defaults.lua     - default settings (to be used by configurations)-config/interactive.lua  - configuration for interactive navigation-config/random.lua       - configuration for random navigation-config/transition.lua   - interpolate between viewports (used by 'random')+config/distance.lua     - compute Mandelbrot exterior distance (used by 'main')+config/main.lua         - 'main' configuration (interact and attract modes)+config/transition.lua   - interpolate between viewports (used by 'main')
NEWS view
@@ -1,3 +1,11 @@+v0.5 2010-07-25 neon in O(1) space++    This version runs in asymptotic constant space.  Aesthetics tweaks+    give more colourful 'neon' output, and the randomized attract mode+    picks more interesting places to explore.++    Source code statistics: 2072 lines, 10050 words, 58399 chars.+ v0.4 2010-07-12 joining the cabal      This version is installable with 'cabal-install'.  Documentation has
README view
@@ -7,23 +7,36 @@ Invocation ---------- -Multi-core CPU with lots of RAM recommended.  For two cores run:+Multi-core CPU with lots of RAM recommended: -mandulia +RTS -N2 -M2G -RTS configuration+    mandulia configuration -Currently there are two preset configurations available:-'interactive' -- keyboard navigation-'random'      -- randomized navigation+Currently there is one preset configurations available:+'main' -- keyboard navigation with randomized attract mode. -Both of these configurations extend 'defaults'.+To render videos (see below): +    mandulia configuration |+    ppmtoy4m -S444 -F25:1 |+    ffmpeg2theora -v 8 -f yuv4mpegpipe -o mandulia.ogv - + Controls -------- -'interactive' defines the following key bindings:+'main' defines the following key bindings:  Escape   -- quit+F11      -- toggle full screen+a        -- enter attract mode immediately+r        -- record for 10 minutes (assuming 25fps)+s        -- record for 1 frame (aka screenshot)++Note that recording is via PPM images on 'stdout', so to avoid flooding+your terminal with binary data be sure to redirect it somewhere else.++As a side-effect, the following count as 'activity':+ PageUp   -- zoom in PageDown -- zoom out cursors  -- move around@@ -31,7 +44,12 @@ Home     -- return to base [ ]      -- change speed #        -- reset speed+{ }      -- change weight+~        -- reset weight +Attract mode is entered after 1 minute (assuming 25fps) of inactivity,+and left on any activity.+ Other configurations may differ, check their sources for details.  @@ -41,14 +59,6 @@ Mandulia can be reconfigured without recompilation, using Lua scripts. The first (non RTS) argument to 'mandulia' will be executed on startup. First it will try to load it as a file, then via Lua's 'require()'.-See 'defaults.lua' to see the available interface, and 'interactive.lua'-for an example of keyboard navigation.  Note that some variables only-take effect on startup, and later modification will be ignored.---Profiling------------If you installed an executable with profiling, try (for example):--mandulia +RTS -s -P -V0.001 -H512M -M2G -RTS+See 'defaults.lua' to see the available interface, and 'main.lua' for an+example of keyboard navigation.  Note that some variables only take+effect on startup, and later modification will be ignored.
config/defaults.lua view
@@ -32,7 +32,11 @@   m.height      =  576        -- (*) window height   m.fps         =   25        --     frames per second   m.detail      =   11        -- (*) detail level-  m.displaysize = m.height/11 --     display size of each Julia set+  if m.width > m.height then  --     display size of each Julia set+    m.displaysize = m.width / 16+  else+    m.displaysize = m.height / 16+  end   m.juliasize   =  256        -- (*) pixel size of each Julia set   m.jobs        =  512        -- (*) maximum number of jobs per frame   m.images      =  512        -- (*) maximum number of images to cache@@ -44,7 +48,11 @@   function m.reshape(width, height)     m.width  = width     m.height = height-    m.displaysize = height / 11+    if width > height then+      m.displaysize = width / 16+    else+      m.displaysize = height / 16+    end   end    -- gets called at the start of each frame
+ config/distance.lua view
@@ -0,0 +1,82 @@+--[[--++Mandulia -- Mandelbrot/Julia explorer+Copyright (C) 2010  Claude Heiland-Allen <claudiusmaximus@goto10.org>++This program is free software: you can redistribute it and/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation, either version 3 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program.  If not, see <http://www.gnu.org/licenses/>.++--]]--++do++  --[[--++  SYNOPSIS++    s = exteriordistance(x, y, n, r)++  INPUTS++    x, y  :  point in the complex plane+    n     :  iteration limit+    r     :  escape radius++  OUTPUTS++    s     :  distance from Mandelbrot set (or nil if inside)++  EXAMPLE++    s = exteriordistance(x, y, 100, 100)+    if s ~= nil and s < 0.01 then ... else ... end++  --]]--+  function exteriordistance(x, y, n, r)++    local px = 0+    local py = 0+    local cx = x+    local cy = y+    local dx = 1+    local dy = 0+    local r2 = r * r+    local escape = false+    local px1, py1, dx1, dy1, px2, py2, pxy, d2, p, d++    for i = 1,n do+      -- p_n+1 := p_n * p_n + c+      -- d_n+1 := 2 * p_n * d_n + 1+      px2 = px * px+      py2 = py * py+      d2 = px2 + py2+      if d2 > r2 then escape = true ; break end+      pxy = px * py+      px1 = px2 - py2 + cx+      py1 = 2 * pxy + cy+      dx1 = 2 * (px * dx - py * dy) + 1+      dy1 = 2 * (dx * py + dy * px)+      px = px1 ; py = py1 ; dx = dx1 ; dy = dy1+    end++    if escape then+      p = math.sqrt(px * px + py * py)+      d = math.sqrt(dx * dx + dy * dy)+      return 2 * p * math.log(p) / d+    else+      return nil+    end++  end++end
− config/interactive.lua
@@ -1,57 +0,0 @@---[[----Mandulia -- Mandelbrot/Julia explorer-Copyright (C) 2010  Claude Heiland-Allen <claudiusmaximus@goto10.org>--This program is free software: you can redistribute it and/or modify-it under the terms of the GNU General Public License as published by-the Free Software Foundation, either version 3 of the License, or-(at your option) any later version.--This program is distributed in the hope that it will be useful,-but WITHOUT ANY WARRANTY; without even the implied warranty of-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-GNU General Public License for more details.--You should have received a copy of the GNU General Public License-along with this program.  If not, see <http://www.gnu.org/licenses/>.----]]----require("defaults")--do--  local delta = { x = 0, y = 0, z = 0 }-  local speed = 0.01-  -  local phi  = (math.sqrt(5) + 1) / 2-  local phi1 = (math.sqrt(5) - 1) / 2-  -  local keys =-    { Escape   = function() mandulia.quit() end-    , F11      = function() mandulia.fullscreen = not mandulia.fullscreen end-    , Right    = function() delta.x = delta.x + 1 end-    , Left     = function() delta.x = delta.x - 1 end-    , Up       = function() delta.y = delta.y + 1 end-    , Down     = function() delta.y = delta.y - 1 end-    , PageUp   = function() delta.z = delta.z + 1 end-    , PageDown = function() delta.z = delta.z - 1 end-    , End      = function() delta         = { x = 0, y = 0, z = 0 } end-    , Home     = function() mandulia.view = { x = 0, y = 0, z = 0 } end-    , ["["]    = function() speed = speed * 0.95 end-    , ["]"]    = function() speed = speed / 0.95 end-    , ["#"]    = function() speed = 0.01 end-    }-  -  function mandulia.render()-    mandulia.view.x = mandulia.view.x + speed * delta.x * phi1 ^ mandulia.view.z-    mandulia.view.y = mandulia.view.y + speed * delta.y * phi1 ^ mandulia.view.z-    mandulia.view.z = mandulia.view.z + speed * delta.z -  end-  -  function mandulia.keyboard(key)-    if type(keys[key]) == "function" then keys[key]() end-  end-  -end
+ config/main.lua view
@@ -0,0 +1,244 @@+--[[--++Mandulia -- Mandelbrot/Julia explorer+Copyright (C) 2010  Claude Heiland-Allen <claudiusmaximus@goto10.org>++This program is free software: you can redistribute it and/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation, either version 3 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program.  If not, see <http://www.gnu.org/licenses/>.++--]]--++require("defaults")+require("transition")+require("distance")++do++  -- frame count without interaction+  local mode = "interact"+  local unattended = 0+  local attentionSpan = 25 * 60++  -- frame counts for recording+  local frames = 0+  local frameLimit = 0++  -- magic numbers+  math.randomseed(os.time())+  local phi  = (math.sqrt(5) + 1) / 2+  local phi1 = (math.sqrt(5) - 1) / 2++  -- animation parameters+  local defaultSpeed = 0.125+  local defaultWeight = 8+  local speed = defaultSpeed+  local weight = defaultWeight++  -- view bounds+  local minimum = { x = -4, y = -4, z =  0 }+  local maximum = { x =  4, y =  4, z = 42 }+  local function clamp(x, mi, ma)+    return math.min(math.max(x, mi), ma)+  end++  -- attract mode for interesting behaviour when unattended+  local attract = { }+  do++    local function walk(x, mi, ma, d)+      local p = (x - mi) / (ma - mi)+      local s = math.random()+      return x + d * (s - p)+    end++    -- default view+    local function zero() return { x = 0, y = 0, z = minimum.z } end++    -- animation variables+    local source = zero()+    local target = zero()+    local t = 0+    local s = 0+    local f = function(t) return zero() end+    local dz = minimum.z++    local history = { }+    for i = 1,48 do+      history[i] = { x = 0, y = 0 }+    end+    local historyptr = 1++    attract.enter = function()+      mode = "attract"+      target.x = mandulia.view.x+      target.y = mandulia.view.y+      target.z = mandulia.view.z+      t = 0+      s = 0+    end++    attract.render = function()+      t = t + speed * (phi1 ^ dz)+      if t > s then -- we reached the target, new target needed+        t = t - s+        source.x = target.x+        source.y = target.y+        source.z = target.z+        local interesting+        local x, y, e, v, d2, dx, dy+        local r = 25 * phi1 ^ dz+        e = exteriordistance(source.x, source.y, 1000, 1000)+        interesting = e ~= nil and e < 0.00001+        if interesting then+          interesting = false+          while not interesting do+            x = source.x + r * (2 * math.random() - 1)+            y = source.y + r * (2 * math.random() - 1)+            e = exteriordistance(x, y, 1000, 1000)+            if e ~= nil and e < 0.00001 then+              v = false+              for i,p in ipairs(history) do+                dx = x - p.x+                dy = y - p.y+                d2 = dx * dx + dy * dy+                v = v or d2 < 0.01+              end+              if v then r = r * 1.1 end+              interesting = not v+            end+          end+        else+          interesting = false+          while not interesting do+            x = math.random() * (maximum.x - minimum.x) + minimum.x+            y = math.random() * (maximum.y - minimum.y) + minimum.y+            e = exteriordistance(x, y, 1000, 1000)+            interesting = e ~= nil and e < 0.00001+          end+        end+        target.x = clamp(x, minimum.x, maximum.x)+        target.y = clamp(y, minimum.y, maximum.y)+        target.z = walk(source.z, minimum.z, maximum.z, 3)+        history[historyptr].x = target.x+        history[historyptr].y = target.y+        historyptr = historyptr + 1+        if historyptr > #history then historyptr = 1 end+        s, f = transition(source, target, weight, phi)+      end+      mandulia.view = f(t)+      mandulia.view.z = clamp(mandulia.view.z, minimum.z, maximum.z)+      dz = mandulia.view.z+    end -- render++  end -- attract++  -- interact mode for engagement and control+  local interact = { }+  do++    local delta = { x = 0, y = 0, z = 0 }++    interact.enter = function()+      mode = "interact"+      unattended = 0+    end++    interact.render = function()+      local x = mandulia.view.x + speed * delta.x * phi1 ^ mandulia.view.z+      local y = mandulia.view.y + speed * delta.y * phi1 ^ mandulia.view.z+      local z = mandulia.view.z + speed * delta.z+      mandulia.view.x = clamp(x, minimum.x, maximum.x)+      mandulia.view.y = clamp(y, minimum.y, maximum.y)+      mandulia.view.z = clamp(z, minimum.z, maximum.z)+    end++    local keys =+      { Right    = function() delta.x = delta.x + 1 end+      , Left     = function() delta.x = delta.x - 1 end+      , Up       = function() delta.y = delta.y + 1 end+      , Down     = function() delta.y = delta.y - 1 end+      , PageUp   = function() delta.z = delta.z + 1 end+      , PageDown = function() delta.z = delta.z - 1 end+      , End      = function() delta         = { x = 0, y = 0, z = 0 } end+      , Home     = function() delta         = { x = 0, y = 0, z = 0 }+                              mandulia.view = { x = 0, y = 0, z = 0 } end+      , ["["]    = function() speed = speed * 0.95 end+      , ["]"]    = function() speed = speed / 0.95 end+      , ["#"]    = function() speed = defaultSpeed end+      , ["{"]    = function() weight = weight * 0.95 end+      , ["}"]    = function() weight = weight / 0.95 end+      , ["~"]    = function() weight = defaultWeight end+      }++    interact.keyboard = function(key)+      if type(keys[key]) == "function" then keys[key]() end+    end++  end -- interact++  local screenshot = { }+  local record = { }+  do++    local recording = false++    screenshot.enter = function()+      frames = 0+      frameLimit = 1+      frameStep = 1+      recording = true+    end++    record.enter = function()+      frames = 0+      frameLimit = 25 * 60 * 10+      frameStep = 1+      recording = true+    end++    record.render = function()+      if recording then+        mandulia.record = (frames % frameStep) == 0 and frames < frameLimit+        frames = frames + 1+      end+    end++  end -- screenshot/record+++  function mandulia.render()+    if mode == "interact" then+      interact.render()+      unattended = unattended + 1+      if unattended >= attentionSpan then attract.enter() end+    elseif mode == "attract" then+      attract.render()+    end+    record.render()+  end++  function mandulia.keyboard(key)+    if     key == "Escape" then mandulia.quit()+    elseif key == "F11"    then mandulia.fullscreen = not mandulia.fullscreen+    elseif key == "a"      then attract.enter()+    elseif key == "r"      then record.enter()+    elseif key == "s"      then screenshot.enter()+    else+      interact.enter()+      interact.keyboard(key)+    end+  end++end++pcall(function() require("config") end)
− config/random.lua
@@ -1,104 +0,0 @@---[[----Mandulia -- Mandelbrot/Julia explorer-Copyright (C) 2010  Claude Heiland-Allen <claudiusmaximus@goto10.org>--This program is free software: you can redistribute it and/or modify-it under the terms of the GNU General Public License as published by-the Free Software Foundation, either version 3 of the License, or-(at your option) any later version.--This program is distributed in the hope that it will be useful,-but WITHOUT ANY WARRANTY; without even the implied warranty of-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-GNU General Public License for more details.--You should have received a copy of the GNU General Public License-along with this program.  If not, see <http://www.gnu.org/licenses/>.----]]----require("defaults")-require("transition")--do--  --[[-- generate random views and transition between them --]]----  -- randomness-  math.randomseed(os.time())--  -- for time-limited recording-  local frameLimit = 10 * 60 * 25-  local frames = 0--  -- magic numbers-  local phi  = (math.sqrt(5) + 1) / 2-  local phi1 = (math.sqrt(5) - 1) / 2--  -- animation parameters-  local defaultSpeed = 0.25-  local defaultWeight = 25--  -- view bounds-  local minimum = { x = -1.5, y = -1.1, z =  1 }-  local maximum = { x =  0.5, y =  1.1, z = 32 }--  -- default view-  local function zero() return { x = 0, y = 0, z = minimum.z } end--  -- animation variables-  local source = zero()-  local target = zero()-  local t = 0-  local s = 0-  local f = function(t) return zero() end-  local dz = minimum.z-  local speed = defaultSpeed-  local weight = defaultWeight--  -- keyboard bindings-  local keys =-    { Escape   = function() mandulia.quit() end-    , F11      = function() mandulia.fullscreen = not mandulia.fullscreen end-    , Home     = function()-                   source = f(t) ; target = zero() ; t = 0-                   s, f = transition(source, target, weight, phi)-                 end-    , PageUp   = function() weight = weight * 0.95 end-    , PageDown = function() weight = weight / 0.95 end-    , End      = function() weight = defaultWeight end-    , ["["]    = function() speed = speed * 0.95 end-    , ["]"]    = function() speed = speed / 0.95 end-    , ["#"]    = function() speed = defaultSpeed end-    }-  function mandulia.keyboard(key)-    if type(keys[key]) == "function" then keys[key]() end-  end--  -- every frame-  function mandulia.render()-    if mandulia.record then-      if frames == frameLimit then-        mandulia.quit()-      else-        frames = frames + 1-      end-    end-    t = t + speed * (phi1 ^ dz)-    if t > s then -- we reached the target, new target needed-      t = t - s-      source.x = target.x-      source.y = target.y-      source.z = target.z-      target.x = math.random() * (maximum.x - minimum.x) + minimum.x-      target.y = math.random() * (maximum.y - minimum.y) + minimum.y-      target.z = math.random() * (maximum.z - minimum.z) + minimum.z-      s, f = transition(source, target, weight, phi)-    end-    mandulia.view = f(t)-    mandulia.view.z = math.max(mandulia.view.z, minimum.z)-    dz = mandulia.view.z-  end--end
mandulia.cabal view
@@ -1,10 +1,14 @@ Name:                mandulia-Version:             0.4+Version:             0.5 Stability:           Provisional Synopsis:            A zooming visualisation of the Mandelbrot Set as many Julia Sets. Description:         Mandulia provides a zooming visualisation of the Mandelbrot Set                      as many Julia Sets.  Featuring a profiled and optimized renderer,                      and a Lua configuration and scripting interface.+                     .+                     By default Mandulia compiles using SSE4 instructions.  If your+                     CPU does not support SSE4, you should add "-f-SSE4" to your+                     cabal-install command line. Homepage:            http://gitorious.org/maximus/mandulia Cabal-version:       >=1.6 License:             GPL-3@@ -17,7 +21,7 @@ Extra-source-files:  AUTHORS ChangeLog HACKING INSTALL NEWS README TODO                      src/rjulia.h src/qsort.h Data-dir:            config-Data-files:          defaults.lua interactive.lua random.lua transition.lua+Data-files:          defaults.lua distance.lua main.lua transition.lua  Flag SSE4   Description:         Enable optimizations for SSE4 CPUs.@@ -66,4 +70,4 @@ Source-repository this   type:                git   location:            git://gitorious.org/maximus/mandulia.git-  tag:                 v0.4+  tag:                 v0.5
src/Bounds.hs view
@@ -45,19 +45,6 @@       V _ y _ = bottomLeft box   in  V x y 1 -{--bounds :: [V] -> Bounds-bounds [] = error "Bounds.bounds []"-bounds vs =-  let xs = map (\(V x _ _) -> x) vs-      ys = map (\(V _ y _) -> y) vs-      x0 = minimum xs-      x1 = maximum xs-      y0 = minimum ys-      y1 = maximum ys-  in  Bounds{ bottomLeft = V x0 y0 1, topRight = V x1 y1 1 }--}- bounds :: [V] -> Bounds bounds [] = error "Bounds.bounds []" bounds (V u v _ : vs) =
src/Interface.hs view
@@ -29,26 +29,30 @@  import Paths_mandulia (getDataFileName) +import Utils(modifyIORef')+ -- interface  data Interface =   Interface-    { iLua         :: LuaState -- initialized in interface-    , iQuit        :: Bool     -- can only be set by Lua 'quit'+    { iLua         :: !LuaState -- initialized in interface+    , iQuit        :: !Bool     -- can only be set by Lua 'quit'     -- these are all updated from Lua globals in Haskell 'update'-    , iRecord      :: Bool-    , iWidth       :: Int-    , iHeight      :: Int-    , iFPS         :: Double-    , iFullScreen  :: Bool-    , iDetail      :: Double-    , iDisplaySize :: Double-    , iJuliaSize   :: Int-    , iJobs        :: Int-    , iImages      :: Int-    , iTextures    :: Int-    , iWorkers     :: Int-    , iView        :: (Double, Double, Double)+    , iRecord      :: !Bool+    , iWidth       :: !Int+    , iHeight      :: !Int+    , iFPS         :: !Double+    , iFullScreen  :: !Bool+    , iDetail      :: !Double+    , iDisplaySize :: !Double+    , iJuliaSize   :: !Int+    , iJobs        :: !Int+    , iImages      :: !Int+    , iTextures    :: !Int+    , iWorkers     :: !Int+    , iView        :: !(Double, Double, Double)+    -- these are updated from the Haskell side, set in Lua in 'update'+    , iStatistics  :: ![(String, (Double, Double, Double))]     }  -- constructor@@ -72,6 +76,7 @@     , iTextures = 2048     , iWorkers = 2     , iView = (0, 0, 0)+    , iStatistics = []     }   Lua.openlibs l   path <- getDataFileName "?.lua"@@ -122,7 +127,7 @@   l <- iLua `fmap` readIORef iR   Lua.close l --- update from Lua+-- update from Lua   FIXME this should copy iStatistics to Lua-land  update :: IORef Interface -> IO () update iR = do@@ -147,7 +152,7 @@   viewX       <- g "view.x"   viewY       <- g "view.y"   viewZ       <- g "view.z"-  modifyIORef iR $ \i ->+  modifyIORef' iR $ \i ->     let f k v = case v of                   Nothing -> k i                   Just x  -> x@@ -171,7 +176,7 @@ -- callbacks from Lua  quit :: IORef Interface -> IO ()-quit iR = modifyIORef iR $ \i -> i{ iQuit = True }+quit iR = modifyIORef' iR $ \i -> i{ iQuit = True }  -- callbacks into Lua 
src/Julia.hs view
@@ -84,7 +84,7 @@   forever $ do     i <- acquire is     withJob js $ \j -> do-      (dtC,_) <- time $ c_julia c (iBuffer i) (realToFrac . jCX . jCoords $ j) (realToFrac . jCY . jCoords $ j)+      (dtC,()) <- time $ c_julia c (iBuffer i) (realToFrac . jCX . jCoords $ j) (realToFrac . jCY . jCoords $ j)       logStats "compute" dtC       return j         { jDoneAction = Just . Right $ (jCoords j, 
src/Mandulia.hs view
@@ -31,7 +31,6 @@ import Graphics.UI.GLUT hiding (scale, translate, fullScreen) import qualified Graphics.UI.GLUT as G import Unsafe.Coerce (unsafeCoerce)---import Numeric (showFFloat)  import Paths_mandulia (version) @@ -52,7 +51,7 @@  data Mandulia =   Mandulia-    { tiling     :: AmmannA3+    { tiling     :: Maybe AmmannA3     , viewMax    :: Bounds     , view       :: Bounds     , width      :: Int@@ -131,16 +130,14 @@       winH   = 1 `max` I.iHeight i       full   = I.iFullScreen i       mspf   = 1 `max` (ceiling $ 1000 / (iFPS i `max` 0.01))-      detail = 1 `max` (ceiling $ iDetail i)       view0  = mkView winW winH 0 0 0-      tiling0 = iterate stepIn (ammannA3 view0) !! detail   (logStats', getStats') <- statsLogger   jobq      <- jobQueue   imgpool   <- resourcePool (image jsize jsize 4) imagen   let texcache = textureCache texn   wtids     <- replicateM workn     (forkIO $ juliaWorker logStats' jsize jsize imgpool jobq)-  manduliaR <- newIORef Mandulia { tiling = tiling0+  manduliaR <- newIORef Mandulia { tiling = Nothing                                  , viewMax = view0                                  , view = view0                                  , width = winW@@ -183,24 +180,15 @@ quit mR = do   m <- readIORef mR   mapM_ killThread (workers m)---  hPutStrLn stderr "what\tcount\tmean\tstddev"---  mapM_ (hPutStrLn stderr . uncurry printStat) =<< getStats m   I.atexit (iface m)   closeInterface (iface m)   exitSuccess -{--printStat :: String -> (Double, Double, Double) -> String-printStat name (n, m, d) =-  name ++ "\t" ++-   show (floor n :: Integer) ++ "\t" ++-    showFFloat (Just 8) m "" ++ "\t" ++-     showFFloat (Just 8) d ""--}- update :: IORef Mandulia -> IO (Julia -> Double) update mR = do   m0 <- readIORef mR+  s <- getStats m0+  modifyIORef' (iface m0) (\i -> i{ iStatistics = s })   I.render (iface m0)   i <- readIORef (iface m0)   when (iQuit i) (quit mR)@@ -210,8 +198,8 @@       v = mkView (width m) (height m) x y z   if v `insideOrEqual` viewMax m    then do-    case zoomTo v (tiling m) of-      Just t  -> do+    case zoomTo v (ammannA3 $ viewMax m) of+      t@(Just _) -> do         writeIORef mR m{ tiling = t, view = v }         return $ score z x y       Nothing -> return $ score z x y -- FIXME should never happen?@@ -235,12 +223,14 @@   i <- readIORef (iface m)   let w = fromIntegral (width m)       h = fromIntegral (height m)-      window = bounds [ V 0 0 1, V w h 1]+      s = iDisplaySize i+      window = bounds [ V (-s) (-s) 1, V (w+s) (h+s) 1]       viewT  = view m `into` window-      ctiles = filter ((==) C . tTile) . tiles 0 . tiling $ m+      ctiles = case tiling m of+        Just t -> filter ((==) C . tTile) . tiles (ceiling d) $ t+        Nothing -> []       d = iDetail i       (_, _, z) = iView i-      s = iDisplaySize i       rads = map ((s *) . radius d z) [0 ..]   return $ partitionEithers         [ case mt of@@ -308,12 +298,12 @@   reprioritise (jobs m) (const js')   return $ filter visible qs -color0 :: Color4 GLclampf-color0 = Color4 1 (0.5 * cos 1 + 0.5) (0.5 * cos 2 + 0.5) 1+black :: Color4 GLclampf+black = Color4 0 0 0 1  drawQuads :: [Quad] -> IO () drawQuads qs = do-  clearColor $= color0+  clearColor $= black   clear [ColorBuffer]   blend $= Enabled   blendFunc $= (SrcAlpha, OneMinusSrcAlpha)@@ -355,12 +345,12 @@   I.reshape (iface m) (fromIntegral w) (fromIntegral h)   i <- readIORef (iface m)   let (x, y, z) = iView i-      s = ceiling $ iDisplaySize i+      s = ceiling $ 2 * iDisplaySize i   modifyIORef mR $ \m' -> m'{ view = mkView (width m') (height m') x y z }   viewport $= (Position (-s) (-s), (Size (w + 2 * fromIntegral s) (h + 2 * fromIntegral s)))   matrixMode $= Projection   loadIdentity-  ortho 0 (fromIntegral w) 0 (fromIntegral h) (-1) 1+  ortho (-fromIntegral s) (fromIntegral w + fromIntegral s) (-fromIntegral s) (fromIntegral h + fromIntegral s) (-1) 1   matrixMode $= Modelview 0   loadIdentity   postRedisplay Nothing
src/StatsLogger.hs view
@@ -54,7 +54,7 @@ statsLogger :: IO (Logger, Statistics) statsLogger = do   v <- newMVar empty-  let logStats name value = modifyMVar_ v $ return . insertWith' combine name (stat value)+  let logStats name value = modifyMVar_ v $ \m -> return $! (insertWith' combine name (stat value) $! m)       getStats = readMVar v >>= return . toAscList . map stats   return (logStats, getStats) 
src/Utils.hs view
@@ -16,24 +16,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. -} -module Utils (phi, phi', clamp) where--{--import Data.List (sortBy)-import Data.Ord (comparing)--first :: (a -> c) -> (a, b) -> (c, b)-first f ~(x,y) = (f x, y)--second :: (b -> c) -> (a, b) -> (a, c)-second f ~(x,y) = (x, f y)--second' :: (b -> c) -> (a, b) -> (a, c)-second' f ~(x,y) = let fy = f y in fy `seq` (x, fy)+module Utils (phi, phi', clamp, modifyIORef') where -sortOn :: Ord o => (a -> o) -> [a] -> [a]-sortOn p = map fst . sortBy (comparing snd) . map (\x -> let px = p x in px `seq` (x, px))--}+import Data.IORef(IORef(), readIORef, writeIORef)  phi  :: Double phi  = (sqrt 5 + 1) / 2@@ -43,3 +28,6 @@  clamp :: Ord r => r -> r -> r -> r clamp mi ma x = ma `min` x `max` mi++modifyIORef' :: IORef a -> (a -> a) -> IO ()+modifyIORef' r m = readIORef r >>= \v -> writeIORef r $! m v
src/Vector.hs view
@@ -60,34 +60,16 @@ (^-^) :: V -> V -> V (V a b c)  ^-^  (V x y z) = V (a-x) (b-y) (c-z) -{- dot :: V -> V -> R-(V a b c) `dot` (V x y z) = sumV $ V (a*x) (b*y) (c*z)--}--dot :: V -> V -> R (V a b c) `dot` (V x y z) = a*x + b*y + c*z -{- (^^*^) :: M -> V -> V-(M a b c) ^^*^  v = V (a`dot`v) (b`dot`v) (c`dot`v)--}--(^^*^) :: M -> V -> V (M (V a b c) (V d e f) (V g h i)) ^^*^ (V r u x) =   V (a * r + b * u + c * x)     (d * r + e * u + f * x)     (g * r + h * u + i * x) -{- (^^*^^) :: M -> M -> M-(M a b c) ^^*^^ m = let M x y z = transposeM m-                    in M (V (a`dot`x) (a`dot`y) (a`dot`z))-                         (V (b`dot`x) (b`dot`y) (b`dot`z))-                         (V (c`dot`x) (c`dot`y) (c`dot`z))--}--(^^*^^) :: M -> M -> M (M (V a b c) (V d e f) (V g h i)) ^^*^^ (M (V r s t) (V u v w) (V x y z)) =   M (V (a * r + b * u + c * x) (a * s + b * v + c * y) (a * t + b * w + c * z))     (V (d * r + e * u + f * x) (d * s + e * v + f * y) (d * t + e * w + f * z))@@ -95,26 +77,3 @@  (^|-|^) :: V -> V -> R u ^|-|^ v = let d = u ^-^ v in sqrt $ d `dot` d--{--import Numeric (showFFloat)--sumV :: V -> R-sumV (V a b c) = a + b + c--identity :: M-identity = M (V 1 0 0) (V 0 1 0) (V 0 0 1)--transposeM :: M -> M-transposeM (M (V a b c) (V d e f) (V g h i)) =-            M (V a d g) (V b e h) (V c f i)--prettyR :: R -> String-prettyR x = (if x < 0 then id else ('+':)) $ showFFloat (Just 6) x ""--prettyV :: V -> String-prettyV (V a b c) = unwords . map prettyR $ [a,b,c]--prettyM :: M -> String-prettyM (M a b c) = unlines . map prettyV $ [a,b,c]--}
src/rjulia.c view
@@ -16,12 +16,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h>-#include <limits.h> #include <math.h>-#include <stdio.h> #include <stdlib.h>-#include <string.h>  #include "rjulia.h" @@ -99,11 +95,35 @@   return x > y ? x : y; } -static inline int colour(R a) {-  int channel = 128 * cos(a) + 128;-  return min(max(channel, 0), 255);+static inline void colour(R v, unsigned char *r, unsigned char *g, unsigned char *b) {+  int rr = 128 * cos(sqrt5 * v) + 128;+  int gg = 128 * cos(sqrt6 * v) + 128;+  int bb = 128 * cos(sqrt7 * v) + 128;+  *r = min(max(rr, 0), 255);+  *g = min(max(gg, 0), 255);+  *b = min(max(bb, 0), 255); } +static inline double mandelbrot1(double x, double y) {+  R px = 0, py = 0, cx = x, cy = y, px1, py1, px2, py2, pxy, d2;+  for (int i = 0; i < 16384; ++i) {+    // p_n+1 := p_n * p_n + c+    // d_n+1 := 2 * p_n * d_n + 1+    px2 = px * px;+    py2 = py * py;+    d2 = px2 + py2;+    if (d2 > escapeRadius2) {+      return i - log2(log2(d2)/log2(escapeRadius2));+    }+    pxy = px * py;+    px1 = px2 - py2 + cx;+    py1 = 2 * pxy + cy;+    px = px1;+    py = py1;+  }+  return -1;+}+ void julia(struct context *ctx, unsigned char *image, double dcx, double dcy) {   if (! ctx || ! image) {     return;@@ -114,10 +134,11 @@   const int stride = width * channels;   const R cx = dcx;   const R cy = dcy;-  const R hw = height * 3.0 / width;-  const R wh = width  * 3.0 / height;-  const R sx = width < height ? 3 : wh;-  const R sy = width < height ? hw : 3;+  const R k = 4;+  const R hw = height * k / width;+  const R wh = width  * k / height;+  const R sx = width < height ? k : wh;+  const R sy = width < height ? hw : k;   const int ntotal = width * height;   int npoints;   { // initialize@@ -141,6 +162,8 @@   int ndone = 0;   int n = 0;   int escapes;+  R u = mandelbrot1(dcx, dcy);+  u = u > 0 ? u : 0;   do {     escapes = 0;     { // iterate@@ -159,11 +182,10 @@             R d = sqrt(zd2);             // colourify pixel             unsigned char *pixel = p->pixel;-            R v = 0.025 * ((n+e) - log2(log2(d)/log2(escapeRadius)));+            R v = (n+e) - log2(log2(d)/log2(escapeRadius));+            v /= 32;+            colour(u + v, &pixel[0], &pixel[1], &pixel[2]);             R a = fmin(fmax(8 * (v - 0.1) * v, 0), 1);-            pixel[0] = colour(sqrt5 * v    );-            pixel[1] = colour(sqrt6 * v + 1);-            pixel[2] = colour(sqrt7 * v + 2);             pixel[3] = a * 255;             p->pixel = 0;             ++escapes;
src/rjulia.h view
@@ -16,8 +16,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. */ -#ifndef JULIA_H-#define JULIA_H 1+#ifndef RJULIA_H+#define RJULIA_H 1  struct context; 
src/rts.c view
@@ -16,4 +16,4 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>. */ -char *ghc_rts_opts = "-M1G"; /* limit heap size to something sane */+char *ghc_rts_opts = "-M50M"; /* we can run in constant space */