# This code generates a stereogram - a way of fooling your eyes into beleiving they are seeing a 3D shape.
# The pattern used as the 'wallpaper strip' is a piece of the mandelbrot set (http://en.wikipedia.org/wiki/Mandelbrot_set) coloured with my own algorithm to make sure that
# each region is busy enough to be usefull for stereogram viewing (this negates the need for the addition of random noise and makes the result look much nicer )
# If you don't know how to look at stereograms it may be hard to see one for the first time on a computer screen, so taking a screenshot and printing it out may help. Also check out
# the methods at http://www.hidden-3d.com/how_to_view_stereogram.php - everyone has a preferred method.
#~ Copyright (c) 2014 Jonathan Whitaker
#~ Permission is hereby granted, free of charge, to any person obtaining a copy
#~ of this software and associated documentation files (the "Software"), to deal
#~ in the Software without restriction, including without limitation the rights
#~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#~ copies of the Software, and to permit persons to whom the Software is
#~ furnished to do so, subject to the following conditions:
#~ The above copyright notice and this permission notice shall be included in
#~ all copies or substantial portions of the Software.
#~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#~ THE SOFTWARE.
# Note: This code does take time to run, but strangely this is not my fault! The turtle starts fast and gradually slows down. This is because when turtle draws it's actually creating
# individual canvas objects on the tkinter canvas which it then has to keep track of. Thus when I draw a 400x800 stereogram pixel by pixel it's keeping track of 320000 rectangles!
# Sadly this can't really be fixed without changing the turtle module (now on my to-do list - this is a usefull educational tool and it's sad that it is no longer maintained...)
import turtle
#from matplotlib import pyplot as plt
# This function returns the iteration number at which z jumps above 2 (see the wikipedia article)
def mandel(zx, zy):
z = zx + zy * 1j
c = z
for i in xrange(400): #max_iterations
if abs(z) > 2.0: break
z = z * z + c
return i
# This generates a strip - a long tall image that will be used to form the repeating pattern of the stereogram (image is represented as a list of lists)
def generate_strip_mandel(width, height):
xstart, ystart, xsize, ysize, xa, xb, ya, yb = 0, 0, 800, 800, -0.2034, -0.1995, -0.8163, -0.8127 # xstart and ystart should be calculated, but lines are precious...
img = []
for y in range(height):
row = []
for x in range(width):
m = 1000*(1-mandel((x * (xb - xa) / xsize + xa),(y * (yb - ya) / ysize + ya))/float(400)) #max ierations - CHANGE
(r,g,b) = ((m-(10*int(m/10)))*0.1, (m-(100*int(m/100)))*0.01, (m-int(m/1000))/1000)
row.append((int(r*255), int(g*255), int(b*255)))
img.append(row)
return img
# I could put this strait in depth_function but I am leaving it for now to help code legibility.
def cos(x):
return (2.718281828459045**(x*1j)).real
# This is the 3D function that will determine the 3D shape you see in the stereogram.
def depth_func(a, b):
return (int(abs(255-(((((a-400)/30.0)*((a-400)/30.0)+((b-340)/30.0)*((b-340)/30.0))**0.5+3*cos((((a-400)/30.0)*((a-400)/30.0)+((b-340)/30.0)*((b-340)/30.0))**0.5)+5-3)*(14)))))
# This is where all the magic happens. Using a thing called the wallpaper effect, we first use generate_strip_mandel() to make an image. We then repeat this image (hence wallpaper).
# Then we distort the wallpaper, giving the illusion of depth when looked at in the right way. This function also returns an image as a list of lists, allowing quick viewing of results with
# matplotlib while I was tuning the parameters.
def gen_stereogram(depth, strips=8, levels=48, zoom = 1):
strip_width = len(depth[0])// (strips - 5)
width = strip_width * strips
height = len(depth)
strip_pix = generate_strip_mandel(strip_width, height)
img_pix2 = [[(0, 0, 0) for i in xrange(width)] for i in xrange(height)]
for y in range(height):
for x in range(strip_width):
img_pix2[y][x] = strip_pix[y][x]
for x in range(strip_width, strip_width*3):
img_pix2[y][x] = img_pix2[y][x-strip_width]
for x in range(len(depth[0])):
depth_offset = round(depth[y][x] / 255.0 * levels) * zoom
tx = x + strip_width*3
img_pix2[y][tx] = img_pix2[y][int(tx-strip_width+depth_offset)]
for x in range(strip_width*3 + len(depth[0]), width):
img_pix2[y][x] = img_pix2[y][x-strip_width]
return img_pix2
print "Generating image - please be patient as this can take up to 30 seconds..."
a = gen_stereogram([[int(depth_func(x, y)) for x in xrange(800)] for y in xrange(800)])
# Uncomment to see the image returned by gen_stereogram in matplotlib (also uncomment 'from matplotlib import pyplot as plot'):
#plt.imshow(a)
#plt.show()
#Begin the turtle stuff
bob = turtle.Turtle()
#speed everything up
bob.ht()
bob.goto(-400, -400)
turtle.ht()
turtle.tracer(5000)
#Black background
bob.pensize(5000)
bob.forward(1)
bob.pensize(1)
#draw the picture. the 2*x and 2*y are to scale it down and make it fit in turtles little 800x640 window
print "Drawing the stereogram. This will take a while..."
for y in range(0, 400, 1):
bob.penup()
bob.goto(-400, y-200)
bob.pendown()
for x in range(0, 800, 1):
# Have to divide by 255 because I used PIL early on to view the output of gen_stereogram and couldn't be bothered to change everything
# The y is negative to flip the image as I think it looks better that way
bob.color(a[-2*y][2*x+200][0]/255.0, a[-2*y][2*x+200][1]/255.0, a[-2*y][2*x+200][2]/255.0)
bob.forward(1)
turtle.exitonclick()