-
-
Save ivanassen/288c44efa63465970a1b to your computer and use it in GitHub Desktop.
Slightly optimized Mandelbrot for Codea
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
supportedOrientations(LANDSCAPE_ANY) | |
-- Mandelbrot set explorer, by Dr. Phillip Alvelda | |
-- it uses a few tricks to speed up the set calcs | |
-- Setup all the variables we'll be using | |
function variableSetup() | |
Color=1 | |
Binary=2 | |
Grayscale=3 | |
RMode = Color -- Default rendering mode. | |
-- Can also be "Grayscale" or "Binary" | |
tileNumber = 100 -- Image resolution. | |
-- Try varying this between 50 to 700 | |
tilesize = WIDTH/tileNumber | |
ManCalcStart = 0 -- Variables track time it takes to calc one line | |
ManCalcEnd = 0 | |
ManCalcTime = 0 | |
LastTouchTime = -1 -- State to time preceeding touch for double tap | |
gx,gy = 0 | |
MinRe = -2.0 -- Window borders in real coordinates | |
MaxRe = 0.7 | |
MinIm = -1.35 | |
MaxIm = 1.35 | |
Re_factor = (MaxRe-MinRe)/(WIDTH -1) -- Scale factors | |
-- =46rom real to window | |
Im_factor = (MaxIm-MinIm)/(HEIGHT -1) | |
MaxIterations = 255 -- Max iteration count for membership check | |
UCounter = 0 | |
count = 1 | |
red={} -- Tables to store the color maps for the set rendering | |
green={} | |
blue={} | |
myImage = image(WIDTH,HEIGHT) -- The raster display image | |
myRow = image(WIDTH,1) | |
startTime = os.clock() | |
end | |
-- Print the initial instructions, setup the color map | |
function setup() | |
setInstructionLimit(0) | |
variableSetup() | |
watch("ManCalcTime") | |
watch("DeltaTime") | |
iparameter("RMode",1, 3,1) | |
iparameter("Resolution",50,700,300) | |
print("This program plots an image of the Mandelbrot set using the image() class.\n") | |
print("Tap anywhere in the window to zoom in, double-tap to zoom out.\n") | |
print("ManCalcTime is the time it takes to calculate the set membership and escape values.\n") | |
print("Parameter: RMode sets the type of color map the set is rendered with.\n") | |
print("Parameter: Resolution sets the level of image detail.") | |
print("Higher resolutions make prettier pictures at the expense of slower update.") | |
InitColorMap() | |
background(0, 0, 0, 255) | |
backingMode(RETAINED) | |
--ManSetCalc() | |
end | |
function InitColorMap() | |
for i=0,MaxIterations do | |
if RMode == Grayscale then -- Grayscale | |
red[i]=255-i*255/MaxIterations | |
green[i]=255-i*255/MaxIterations | |
blue[i]=255-i*255/MaxIterations | |
elseif RMode == Color then | |
-- Color gradients designed to use their prime | |
-- number mulltiples to create a range of varied | |
-- and uniqe color bands, even when MaxIterations | |
-- gets very high. | |
red[i]=(i*11) % 255 | |
green[i] = (i*5) % 255 | |
blue[i] = (i * 7) % 255 | |
elseif RMode == Binary then | |
-- Alternating black and white | |
if (i % 2) == 0 then | |
red[i]=255 | |
green[i]=255 | |
blue[i]=255 | |
else | |
red[i]=0 | |
green[i]=0 | |
blue[i]=0 | |
end | |
end | |
end | |
end | |
function touched(touch) | |
if touch.state == ENDED then | |
--only use the final touch event to scale image window | |
dre=MaxRe-MinRe | |
dim=MaxIm-MinIm | |
gx = MinRe + touch.x/WIDTH * dre | |
gy = MinIm + (touch.y)/HEIGHT * dim | |
TouchTime = os.clock() | |
if TouchTime - LastTouchTime < 0.3 then | |
-- zoom out if double tap | |
MinRe = gx - dre * 1.33 | |
MaxRe = gx + dre * 1.33 | |
MinIm = gy - dim * 1.33 | |
MaxIm = gy + dim * 1.33 | |
else | |
--set the zoom in factor and new window borders | |
MinRe = gx - dre / 3 | |
MaxRe = gx + dre / 3 | |
MinIm = gy - dim / 3 | |
MaxIm = gy + dim / 3 | |
end | |
Re_factor = (MaxRe-MinRe)/(WIDTH - 1) | |
Im_factor = (MaxIm-MinIm)/(HEIGHT - 1) | |
LastTouchTime = TouchTime | |
count=1 | |
end | |
end | |
function ManSetCalc() | |
ManCalcStart= os.clock() | |
local y, x, n, isInside | |
local imf=Im_factor*tilesize | |
local ref=Re_factor*tilesize | |
local iterations | |
setContext(myRow) | |
background(red[MaxIterations], | |
green[MaxIterations], | |
blue[MaxIterations]) | |
setContext() | |
-- note each call to ManSetCalc() only updates | |
-- a single line of the image at y=count | |
y = myImage.height - count | |
local c_im = MinIm + y*imf | |
for x = 1, tileNumber-1 do | |
local c_re = MinRe + x*ref | |
--check to see if x,y are inside the main | |
--cardioid or period2 bulb | |
--in which case they are in the set and | |
--no iterative check is necessary | |
--results in a 5x speedup when zoomed out | |
q=(c_re-0.25)*(c_re-0.25) + c_im*c_im | |
if ( ((c_re+1)*(c_re+1) + c_im * c_im < 0.0625) or | |
(q*(q+(c_re-0.25))<0.25*c_im*c_im) ) then | |
--[[ myImage:set(x,y,red[MaxIterations], | |
green[MaxIterations], | |
blue[MaxIterations],255)]] | |
else | |
-- Iterate the Mandelbrot set function from the starting | |
-- position on the complex plane to see if it stays | |
-- inside a radius less than 2 units from the origin. | |
local Z_re = c_re | |
local Z_im = c_im | |
isInside = true | |
iterations=MaxIterations | |
for n=0, MaxIterations, 1 do | |
local Z_re2 = Z_re*Z_re | |
local Z_im2 = Z_im*Z_im | |
-- Note the radius limit is 4 here after a rewrite | |
-- to avoid having a sqrt() in the inner loop. | |
if Z_re2 + Z_im2 > 4 then | |
iterations = n | |
break | |
end | |
Z_im = 2*Z_re*Z_im + c_im | |
Z_re = Z_re2 - Z_im2 + c_re | |
end | |
-- Store the iteration result in the image buffer | |
myRow:set(x,1,red[iterations], | |
green[iterations], | |
blue[iterations],255) | |
end | |
end | |
ManCalcEnd = os.clock() | |
ManCalcTime = ManCalcEnd-ManCalcStart | |
end | |
function draw() | |
smooth() -- noSmooth() if you like a blockier image | |
-- If RMode iparamter changes reinitialize the colormap | |
if RMode ~= LastRMode then | |
InitColorMap() | |
LastRMode = RMode | |
end | |
-- If Resolution iparameter changes then reinitialize image buffer | |
if Resolution ~= LastResolution then | |
tileNumber=Resolution | |
tilesize = WIDTH/tileNumber | |
-- Redefine image buffer to new resolution | |
myImage = image(tileNumber+1,tileNumber+1) | |
myRow = image(tileNumber+1, 1) | |
-- Reset rendering to top of image | |
-- to be sure count is within buffer bounds | |
count=1 | |
LastResolution = Resolution | |
background(0,0,0) | |
end | |
-- Render the image buffer | |
spriteMode(CORNER) | |
sprite(myRow, 0, HEIGHT-count*tilesize, WIDTH, tilesize) | |
-- Calculate and store current line of image data | |
ManSetCalc() | |
count = 1 + count % tileNumber | |
if count == tileNumber then | |
newTime = os.clock() | |
print(newTime - startTime) | |
startTime = newTime | |
-- print(os.difftime(os.time(), startTime)) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment