# PREAMBLE
# My original intention was to create some
# disruptive visual camoflage, inspired by the pattern on
# the bark of plane trees. The result was never quite what I intended
# and several fixes had to be incorperated to aproximate what I wanted.
# The end result is effective though and reminds me of a Jackson Pollock
# or a Monet painting.
# I think that the program does quite a good job of producing an aesteticalty
# pleasing picture, through a purely mathematical process,
# though some considerable tweeking on the part of the programmer
# was needed to achieve this.
# In this version, I am able to show a wide variety of colour schemes.
# Artists code their colours according to three dimensions, (hue, chorma and saturation)
# This might be a more apropriate approach than (red, green, blue).
# CODE
from Tkinter import *
from math import *
from random import*
# critical parameters, adjust to suit
W = 800 # canvas dimensions
H = 500 # golden ratio
nLow = 25 # recursive limiter
nCover = 0.05 # Adjusts probability of a particular area being painted over per sweep
nMSpan = 10.0 # Same as above, These two parameters depend upon the number of recursions
nCSpan = 8.0 # Same for colour range
nSplatterSize = 0.25
# scale factor per recursion. i.e. not scale invariant
aScale = [0.05, .1, .6, 0.9,0.9,0.3,0.1,0.1,0.1,0,0,0,0,0,0,0,0,0,0,0,0]
# colours
aColour = [0.5,0.0,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]
# The two functions VibrantHousePaint and DrawDrip
# can be modified to suit
def VibrantHousePaint(oM, nR):
# colour scheme
rg = oM.rg
rb = oM.rb
gb = oM.gb
cR = ColStr(int((ZeroToOne(oM.r + rg - rb - oM.rgb , nCSpan * nR)) * nMaxR) + nMinR) # red
cG = ColStr(int((ZeroToOne(oM.g - rg + gb - oM.rgb , nCSpan * nR)) * nMaxG) + nMinG) # green
cB = ColStr(int((ZeroToOne(oM.b + gb - rb - oM.rgb , nCSpan * nR)) * nMaxB) + nMinB) # blue
return '#' + cR + cG + cB
def DrawDrip(nX, nY, oM, nR, bScheme):
colour = apply(bScheme, (oM, nR))
nL = (nLow + 0.0) * nR / 3
nX1 = dist(nX, nL * nSplatterSize)
nY1 = dist(nY, nL * nSplatterSize)
nL2 = dist(nL , nL)
canvas.create_oval( nX1, nY1, nX1 + nL2, nY1 + nL2, fill = colour, width = 0)
canvas.create_rectangle( nX1, nY1 , nX1 + nL2 / 2.0, nY1 + nL2 / 2.0, fill = colour, width = 0)
canvas.update()
class splat:
def __init__(self,z,r,g,b,rg,rb,gb,rgb):
self.z = z
self.r = r
self.g = g
self.b = b
self.rg = rg
self.rb = rb
self.gb = gb
self.rgb = rgb
def ColStr( x):
if x > 255:
x = 0
if x < 0:
x = 255
s = "%x" % x # converts x into a hexidecimal string
if len( s) < 2:
s = '0' + s
return s
def Load(aGrid, nX, nY, n):
# changes value if not set, or else it returns the value
cKey = str( nX) + '_' + str( nY)
if aGrid.has_key(cKey):
return aGrid[cKey]
aGrid[cKey] = n
return n
def ZeroToOne(nM, nSpan):
# maps the Real domain onto [0 , 1]
return atan(nM * nSpan) / pi + 0.5
def mid(n1, n2):
return int((n1 + n2) / 2)
def dist(nP, nScale):
return nP + (random() - 0.5) * nScale
def odist(A, nS1, nS2):
z = 0
r = 0
g = 0
b = 0
h = 0
v = 0
rg = 0
rb = 0
gb = 0
rgb = 0
for i in A:
z += i.z
r += i.r
g += i.g
b += i.b
rg += i.rg
rb += i.rb
gb += i.gb
rgb += i.rgb
l = len(A)
z = dist(z / l, nS1)
r = dist(r / l, nS2)
g = dist(g / l, nS2)
b = dist(b / l, nS2)
rg = dist(rg / l, nS2)
rb = dist(rb / l, nS2)
gb = dist(gb / l, nS2)
rgb = dist(rgb / l, nS2)
return splat(z, r, g, b, rg, rb, gb, rgb)
def FracDown(aGrid, nX1, nY1, nX2, nY2, oTL, oTR, oBL, oBR, nLim, nRecursive, bScheme):
# fractal lanscape grenerator
dx = nX2 - nX1
dy = nY2 - nY1
nS = aScale[nRecursive]
nSC = aColour[nRecursive]
oT = odist([oTL, oTR], nS * nHorizFactor, nSC)
oL = odist([oTL, oBL], nS, nSC)
oR = odist([oTR, oBR], nS, nSC)
oB = odist([oBL, oBR], nS * nHorizFactor, nSC)
oM = odist([oTL, oTR, oBL, oBR], nS * nDiagFactor, nSC)
nXm = mid(nX1, nX2)
nYm = mid(nY1, nY2)
oTL = Load(aGrid, nX1, nY2, oTL)
oTR = Load(aGrid, nX2, nY2, oTR)
oBL = Load(aGrid, nX1, nY1, oBL)
oBR = Load(aGrid, nX1, nY1, oBR)
if dx <= nLow and dy <= nLow:
if ZeroToOne(oM.z, nMSpan * sqrt(nRecursive)) > nLim:
DrawDrip(nXm, nYm, oM, sqrt(nRecursive), bScheme)
return
t1 = (aGrid, nX1, nYm, nXm, nY2, oTL, oT, oL, oM, nLim, nRecursive + 1, bScheme)
t2 = (aGrid, nXm, nYm, nX2, nY2, oT, oTR, oM, oR, nLim, nRecursive + 1, bScheme)
t3 = (aGrid, nX1, nY1, nXm, nYm, oL, oM, oBL, oB, nLim, nRecursive + 1, bScheme)
t4 = (aGrid, nXm, nY1, nX2, nYm, oM, oR, oB, oBR, nLim, nRecursive + 1, bScheme)
aT = [t1,t2,t3,t4]
shuffle(aT)
for i in aT:
apply(FracDown, i)
def ColourRange():
nMin = int(random() * 255)
nMax = int(random() * 255)
if nMin > nMax:
nMin, nMax = nMax, nMin
return nMin, nMax - nMin
def Frac(nLim, bScheme):
aGrid = {}
s1 = splat(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0)
s2 = splat(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0)
s3 = splat(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0)
s4 = splat(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0)
FracDown(aGrid, 0, 0, W - 1, H - 1, s1, s2, s3, s4,nLim, 1, bScheme)
seed()
canvas = Canvas( width = W, height = H)
canvas.pack(side = TOP)
canvas.create_rectangle( 0, 0, W, H, fill = 'gray', width = 0)
nHorizFactor = (W + 0.0) / (H + 0.0)
nDiagFactor = sqrt(H**2 + W**2) / (H + 0.0)
nMinR, nMaxR = ColourRange()
nMinG, nMaxG = ColourRange()
nMinB, nMaxB = ColourRange()
Frac(nCover, VibrantHousePaint)
print 'done'