Very fast mandelbrot explorer

So I had an idea to make a random coloring thing just to be a pretty effect and whatnot, and I’ve gotten one that works somewhat okayish, but it doesn’t work CORRECTLY. It’s supposed to sample already drawn pixels and average them to come up with its own color, but as you can see with the first line alone, it doesn’t work. Then the rest of the lines somewhat work, but they don’t sample from the first line and there are random lightly colored ones scattered about (on the bright side, the sampling works properly from the random light ones). Below is the code

``````function setup()
img = image(300,300)
co = coroutine.create(makerand)
end
function draw()
noSmooth()
background(40, 40, 50)
coroutine.resume(co)
sprite(img,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
end
function makerand()
for x=1,img.width do
for y=1,img.height do
local cols = {}
local colsx = {x,x-1,x-2}
local colsy = {y-1,y+1,y-2,y+2}
for a,b in pairs(colsx) do
for q,l in pairs(colsy) do
if l > 1 and l < img.height and b > 1 then
local tempcol = color(0,0,0,0)
tempcol.r,tempcol.g,tempcol.b,tempcol.a = img:get(b,l)
if tempcol.a > 0 then
cols[#cols+1]=tempcol
end
end
end
end
local finalcol
if not cols[1] then
finalcol = color(math.random(255),math.random(255),math.random(255),255)
else
finalcol = color(0,0,0,255)
for z,v in pairs(cols) do
finalcol.r = finalcol.r + v.r +math.random(-2,2)
finalcol.g = finalcol.g + v.g +math.random(-2,2)
finalcol.b = finalcol.b + v.b +math.random(-2,2)
end
finalcol.r = finalcol.r / #cols
finalcol.g = finalcol.g / #cols
finalcol.b = finalcol.b / #cols
end
img:set(x,y,finalcol)
end
if x%3==0 then coroutine.yield() end
end
end
``````

@Monkeyman32123 Nice idea. I didn’t feel like trying to figure out why you’re code doesn’t work, so I wrote my own similar to yours. This is what yours might look like.

``````
displayMode(FULLSCREEN)

function setup()
img=image(500,500)
co = coroutine.create(getAverage)
end

function draw()
background(0)
coroutine.resume(co)
sprite(img,WIDTH/2,HEIGHT/2)
end

function getAverage()
for x=1,img.width do
for y=1,img.height do
colsx = {x-1,x-2}
colsy = {y-1,y+1,y-2,y+2}
if x<3 then
img:set(x,y,math.random(255),math.random(255),math.random(255))
else
ra,ga,ba=0,0,0
for xx=1,#colsx do
for yy=1,#colsy do
cx=colsx[xx]
cy=colsy[yy]
if cx>0 and cx<=img.width and cy>0 and cy<=img.width then
r,g,b=img:get(cx,cy)
ra=ra+r
ga=ga+g
ba=ba+b
end
end
end
img:set(x,y,math.floor(ra/8),math.floor(ga/8),math.floor(ba/8))
end
end
if x%5==0 then
coroutine.yield()
end
end
end

``````

@Monkeyman32123 Here’s your code corrected. I added displayMode, and changed 2 lines. Those lines are marked with the comment – changed.

EDIT: Writing my own code first helped me debug the original code since I knew what was being done.

``````

function setup()
img = image(300,300)
co = coroutine.create(makerand)
end
function draw()
noSmooth()
background(40, 40, 50)
coroutine.resume(co)
sprite(img,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
end
function makerand()
for x=1,img.width do
for y=1,img.height do
local cols = {}
local colsx = {x-1,x-2} -- changed
local colsy = {y-1,y+1,y-2,y+2}
for a,b in pairs(colsx) do
for q,l in pairs(colsy) do
if l > 1 and l < img.height and b > 1 then
local tempcol = color(0,0,0,0)
tempcol.r,tempcol.g,tempcol.b,tempcol.a = img:get(b,l)
if tempcol.a > 0 then
cols[#cols+1]=tempcol
end
end
end
end
local finalcol
if x<3 then -- changed
finalcol = color(math.random(255),math.random(255),math.random(255),255)
else
finalcol = color(0,0,0,255)
for z,v in pairs(cols) do
finalcol.r = finalcol.r + v.r +math.random(-2,2)
finalcol.g = finalcol.g + v.g +math.random(-2,2)
finalcol.b = finalcol.b + v.b +math.random(-2,2)
end
finalcol.r = finalcol.r / #cols
finalcol.g = finalcol.g / #cols
finalcol.b = finalcol.b / #cols
end
img:set(x,y,finalcol)
end
if x%3==0 then coroutine.yield() end
end
end

``````

Thank you for both! Both exactly what I meant to do! If you don’t mind explaining, what made those corrections make it work?

Also, here is another version that, instead of a side bleed, uses points about the image to color the pixels. Makes a nice lava Lampy effect

``````

function setup()
img = image(500,500)
co = coroutine.create(makerand)
ps,pc = {},{}
end
function draw()
noSmooth()
background(40, 40, 50)
coroutine.resume(co)
sprite(img,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
end
function makerand()
makepointsources()
for x=1,img.width do
for y=1,img.height do
local r = 0
local g = 0
local b = 0
local usenum = 0
for i=1,#ps do
local pcol,ppos = pc[i],ps[i]
dist = vec2(x,y):dist(vec2(ppos.x,ppos.y))
if dist > img.width/4 then
dist = img.width/4
else
usenum = usenum + (img.width/4-dist)
end
dist = img.width/4-dist
r = r + (dist*pcol.r)
g = g + (dist*pcol.g)
b = b + (dist*pcol.b)
end
--print(r,g,b)
r=r/usenum
g=g/usenum
b=b/usenum
img:set(x,y,r,g,b,255)
end
coroutine.yield()
end
end
function makepointsources()
for i=1,math.pow(img.width,7/12) do
local x1 = math.random(img.width)
local y1 = math.random(img.height)
ps[i] = {x=x1,y=y1}
pc[i] = color(math.random(255),math.random(255),math.random(255),255)
img:set(x1,y1,pc[i])
end
end
``````

Not the same but I didn’t want to make a new thread ( I have enough threads already). I made a very simple mandelbrot explorer. It would be nice to implement double the precision into the OpenGL half of the process so that it can zoom further (by using two floats to represent each number), but the way to do that is far beyond my comprehension. The mandelbrot set has been a fascination of mine for a while, so I researched algorithms and decided to use this one (and took the method for coloring from the codea built in example. Tap to zoom in where you tapped, zoom out action parameter to zoom out. The maxiter parameter is actually 2^maxiter (so it runs very slowly at higher numbers). If any of you know how to implement double the precision into the GLSL half and are also very bored (or just love the fractal as much as I do) I would literally love you forever (I could explore mandelbrot for days as is, but I always get depressed once I reach what I call the “pixel point”). Anyways, rant over, enjoy

``````-- Mandelshader
displayMode(OVERLAY)
-- Use this function to perform your initial setup
function setup()
m = mesh()
img = image(WIDTH,HEIGHT)
m.texture = img
c3 = c2-c1
refresh = true
parameter.action("zoom_out",function() touched({x=WIDTH/2,y=HEIGHT/2,state=ENDED},1.5) end)
parameter.integer("maxiter",1,10,7,function() m.shader.maxiter = 2^maxiter; refresh = true end)
parameter.integer("rn",1,33,3,function() m.shader.rn = rn; refresh = true end)
parameter.integer("gn",1,33,5,function() m.shader.gn = gn; refresh = true end)
parameter.integer("bn",1,33,7,function() m.shader.bn = bn; refresh = true end)
end
function draw()
background(40, 40, 50)
if refresh then
setContext(img)
m:draw()
setContext()
refresh  = false
end

sprite(img,WIDTH/2,HEIGHT/2)

end
function touched(t,diff)
if t.state == ENDED then
local x = c1.x+c3.x*(t.x/WIDTH)
local y = c1.y+c3.y*(t.y/HEIGHT)
diff = c3*(diff or (1/4))
c1 = vec2(x-diff.x,y-diff.y)
c2 = vec2(x+diff.x,y+diff.y)
c3 = c2-c1
refresh = true
end
end
f=[[
//
//

//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform highp vec2 coords;
uniform highp vec2 coords2;
uniform int maxiter;
uniform float rn;
uniform float gn;
uniform float bn;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
highp vec2 coords3 = coords2-coords;
float x0 = coords.x+coords3.x*(vTexCoord.x);
float y0 = coords.y+coords3.y*(vTexCoord.y);
float x = x0;
float y = y0;
int iteration = 0;
while ( ((x*x + y*y) < 4.) && (iteration < maxiter)) {
highp float xtemp = x*x - y*y + x0;
y = 2.*x*y + y0;
x = xtemp;
iteration = iteration + 1;
}
float c = float(iteration)/float(maxiter);
//float r = mod(c*3.,1.);
//float g = mod(c*5.,1.);
//float b = mod(c*7.,1.);
float r = mod(c*rn,1.);
float g = mod(c*gn,1.);
float b = mod(c*bn,1.);
lowp vec4 col = vec4(r,g,b,1.);
//imag:set(xa, ya, col)
//Sample the texture at the interpolated coordinate
//lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;

//Set the output color to the texture color
gl_FragColor = col;
}
]],
v=[[
//
//

//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;

//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;

//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}

]]
}
``````

Here’s a (much slower) version with emulated double precision. Took me awhile, but it is much prettier.

``````supportedOrientations(LANDSCAPE_ANY)
displayMode(OVERLAY)
function setup()
m = mesh()
sf = 1 --change to 2 for higher detail at the cost of 4x the load time
img = image(WIDTH*sf,HEIGHT*sf)
c1 = vec2(-2,-1)
c2 = vec2(.5,1)
c3 = c2-c1
refresh(0)
parameter.action("zoom_out",function() touched({x=WIDTH/2,y=HEIGHT/2,state=ENDED},1.5) end)
parameter.integer("maxiter",2,11,2,function() m.shader.maxiter = 2^maxiter; refresh(0) end)
parameter.integer("rn",1,33,3,function() m.shader.rn = rn; refresh(0) end)
parameter.integer("gn",1,33,5,function() m.shader.gn = gn; refresh(0) end)
parameter.integer("bn",1,33,7,function() m.shader.bn = bn; refresh(0) end)
print("maxiter is actually 2^maxiter \
\
rn,gn,and bn are the amount of red, green, and blue respectively\
\
tap to zoom in on the touch point")
end
function draw()
background(40, 40, 50)
if ref then
setContext(img)
m:draw()
setContext()
end
sprite(img,WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
refresh()
end
function refresh(set)
if set then
rnum = set
ref = true
else
rnum = rnum + 1
if rnum == (2^maxiter*sf)+2 then
ref = false
end
end
end
function touched(t,diff)
if t.state == ENDED then
local x = c1.x+c3.x*(t.x/WIDTH)
local y = c1.y+c3.y*(t.y/HEIGHT)
diff = c3*(diff or (1/4))
c1 = vec2(x-diff.x,y-diff.y)
c2 = vec2(x+diff.x,y+diff.y)
c3 = c2-c1
refresh(1)
end
end
f=[[
//
//

//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform highp vec2 coordsx,coordsy;
uniform highp vec2 coords2x,coords2y;
uniform int maxiter;
uniform float rn;
uniform float gn;
uniform float bn;
uniform float num;
uniform float num2;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
vec2 dsadd (vec2 dsa, vec2 dsb)
{
vec2 dsc;
float t1, t2, e;

t1 = dsa.x + dsb.x;
e = t1 - dsa.x;
t2 = ((dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y + dsb.y;

dsc.x = t1 + t2;
dsc.y = t2 - (dsc.x - t1);
return dsc;
}
vec2 dsmultiply (vec2 dsa, vec2 dsb)
{
vec2 dsc;
float c11, c21, c2, e, t1, t2;
float a1, a2, b1, b2, cona, conb, split = 8193.;
cona = dsa.x * split;
conb = dsb.x * split;
a1 = cona - (cona - dsa.x);
b1 = conb - (conb - dsb.x);
a2 = dsa.x - a1;
b2 = dsb.x - b1;
c11 = dsa.x * dsb.x;
c21 = a2 * b2 + (a2 * b1 + (a1 * b2 + (a1 * b1 - c11)));
c2 = dsa.x * dsb.y + dsa.y * dsb.x;
t1 = c11 + c2;
e = t1 - c11;
t2 = dsa.y * dsb.y + ((c2 - e) + (c11 - (t1 - e))) + c21;
dsc.x = t1 + t2;
dsc.y = t2 - (dsc.x - t1);
return dsc;
}
vec2 dssub (vec2 dsa, vec2 dsb)
{
vec2 dsc;
float e, t1, t2;
t1 = dsa.x - dsb.x;
e = t1 - dsa.x;
t2 = ((-dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y - dsb.y;
dsc.x = t1 + t2;
dsc.y = t2 - (dsc.x - t1);
return dsc;
}
// Compare: res = -1 if a < b
//              = 0 if a == b
//              = 1 if a > b
float dscompare(vec2 dsa, vec2 dsb)
{
if (dsa.x < dsb.x) return -1.;
else if (dsa.x == dsb.x)
{
if (dsa.y < dsb.y) return -1.;
else if (dsa.y == dsb.y) return 0.;
else return 1.;
}
else return 1.;
}
void main()
{
if (vTexCoord.x > num && vTexCoord.x < num2) {
highp vec2 coords3x = dssub(coords2x,coordsx);
highp vec2 coords3y = dssub(coords2y,coordsy);
highp vec2 x = x0;
highp vec2 y = y0;
int iteration = 0;
while ( (dscompare(dsadd(dsmultiply(x,x),dsmultiply(y,y)),vec2(4.,0.)) == -1.) && (iteration < maxiter)) {
highp vec2 xtemp = dsadd(dssub(dsmultiply(x,x),dsmultiply(y,y)) , x0);
x = xtemp;
iteration = iteration + 1;
}
float c = float(iteration)/float(maxiter);
float r = mod(c*rn,1.);
float g = mod(c*gn,1.);
float b = mod(c*bn,1.);
lowp vec4 col = vec4(r,g,b,1.);
gl_FragColor = col;
}
else {
}
}
]]
,
v=[[
//
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
//multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]]
}
``````