mandulia 0.4 → 0.5
raw patch · 20 files changed
+506/−324 lines, 20 files
Files
- ChangeLog +38/−2
- HACKING +3/−3
- NEWS +8/−0
- README +28/−18
- config/defaults.lua +10/−2
- config/distance.lua +82/−0
- config/interactive.lua +0/−57
- config/main.lua +244/−0
- config/random.lua +0/−104
- mandulia.cabal +7/−3
- src/Bounds.hs +0/−13
- src/Interface.hs +23/−18
- src/Julia.hs +1/−1
- src/Mandulia.hs +16/−26
- src/StatsLogger.hs +1/−1
- src/Utils.hs +5/−17
- src/Vector.hs +0/−41
- src/rjulia.c +37/−15
- src/rjulia.h +2/−2
- src/rts.c +1/−1
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 */