mandulia-0.5: config/main.lua
--[[--
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)