# 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. # CODE from Tkinter import * from math import * from random import* # critical parameters, adjust to suit W = 900 # canvas dimensions H = 500 nLow = 25 # recursive limiter nLayers = 5 # number of repeated paint overs nCover = 0.8 # Adjusts probability of a particular area being painted over per sweep nMSpan = 5.0 # Same as above, These two parameters depend upon the number of recursions nCSpan = 2.0 # Same for colour range # scale factor per recursion. i.e. not scale invariant aScale = [0.1, .3, .5, 0.5,0.9,0.9,0.1,0.1,0.1,0,0,0,0,0,0,0,0,0,0,0,0] # colours are scale invariant aColour = [0.5,0.5,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 AutumnPastels and DrawSplash # can be modified to suit def AutumnPastels(nCR, nCG, nCB): # colour scheme cR = ColStr(int((ZeroToOne(nCR, 8.0)) * 150) + 80) # red cG = ColStr(int((ZeroToOne(nCG, 8.0)) * 150) + 80) # green cB = ColStr(int((ZeroToOne(nCB, 8.0)) * 25) + 30) # blue return '#' + cR + cG + cB def DrawSplash(nX, nY, colour): # simulation of paint splatter or leaves nX2 = dist(nX, nLow / 2.0) nY2 = dist(nY, nLow / 2.0) for i in range(8): nX1 = dist(nX2, nLow / 3.0) nY1 = dist(nY2, nLow / 3.0) nL = dist(nLow / 6.0 + 2, nLow / 3.0) canvas.create_oval( nX1, nY1, nX1 + nL, nY1 + nL, fill = colour, width = 0) canvas.update() 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 # This is a clumbsy way of recording continuity across the plane # A dictionary would be more efficient, but it seems to work O.K. def Load(aGrid, nX, nY, n): # changes value if not set, or else it returns the value if aGrid[nX][nY] == -1: aGrid[nX][nY] = n return n return aGrid[nX][nY] 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 FracDown(aGrid, nX1, nY1, nX2, nY2, nTL, nTR, nBL, nBR, nLim, nRecursive, nCR, nCG, nCB): # fractal lanscape grenerator dx = nX2 - nX1 dy = nY2 - nY1 nS = aScale[nRecursive] nT = dist((nTL + nTR) / 2, nS * nHorizFactor) nL = dist((nTL + nBL) / 2, nS) nR = dist((nTR + nBR) / 2, nS) nB = dist((nBL + nBR) / 2, nS * nHorizFactor) nM = dist((nTL + nTR + nBL + nBR) / 4, nS * nDiagFactor) nSC = aColour[nRecursive] nCR = dist(nCR, nSC) nCG = dist(nCG, nSC) nCB = dist(nCB, nSC) nXm = mid(nX1, nX2) nYm = mid(nY1, nY2) if dx <= nLow and dy <= nLow: if ZeroToOne(nM, nMSpan) > nLim: DrawSplash(nXm, nYm, AutumnPastels(nCR, nCG, nCB)) return nTL = Load(aGrid, nX1, nY2, nTL) nTR = Load(aGrid, nX2, nY2, nTR) nBL = Load(aGrid, nX1, nY1, nBL) nBR = Load(aGrid, nX1, nY1, nBR) t1 = (aGrid, nX1, nYm, nXm, nY2, nTL, nT, nL, nM, nLim, nRecursive + 1, nCR, nCG, nCB) t2 = (aGrid, nXm, nYm, nX2, nY2, nT, nTR, nM, nR, nLim, nRecursive + 1, nCR, nCG, nCB) t3 = (aGrid, nX1, nY1, nXm, nYm, nL, nM, nBL, nB, nLim, nRecursive + 1, nCR, nCG, nCB) t4 = (aGrid, nXm, nY1, nX2, nYm, nM, nR, nB, nBR, nLim, nRecursive + 1, nCR, nCG, nCB) aT = [t1,t2,t3,t4] shuffle(aT) for i in aT: apply(FracDown, i) def Frac(nLim): aGrid = [] for i in range(W): aGrid.append([-1] * H) FracDown(aGrid, 0, 0, W - 1, H - 1, 0.0, 0.0, 0.0, 0.0,nLim, 1, 0.0, 0.0, 0.0) seed() canvas = Canvas( width = W, height = H) canvas.pack(side = TOP) canvas.create_rectangle( 0, 0, W, H, fill = AutumnPastels(-0.8, -0.4, -3.0), width = 0, tag ='o') nHorizFactor = (W + 0.0) / (H + 0.0) nDiagFactor = sqrt(H**2 + W**2) / (H + 0.0) for i in range(nLayers): Frac(nCover) print 'done'