Does not relate directly to the panda3d, but can be useful if you want to generate a map consisting of several types of surfaces. For example water, earth and mountains.
The algorithm is abstracted from any specific implementation of a map, and therefore need to pass two callback functions to work with your version. The first function should accept a tuple (x, y) and return surface type, located at xy.
The second - should take a tuple (x, y), surface type and set this type to the xy coordinate
The class constructor accepts the following parameters:
sample - a list or tuple of surface types. Elements of any type.
getCallback - pointer to the first callback function
setCallback - pointer to the second callback function
dimension - size of the map (x, y)
step - the number of cells that will divide the field at each iteration. Must be > 1. Size of the map should be divisible by step. Ideally - must be a power of step.
generate method takes 2 parameters and returns True if generation successful:
showProgress - show progress of generation (0 - no; 1 - simple (default); 2 - advanced output is only supported on unix-based terminals)
maxIters - limitation of iterations (0 - maximum number of iterations (default); an integer> 0 - restrict the number of iterations; an integer <0 - deducted from the maximum possible number of iterations)
You can assign a template to generate the maps in the form of two-dimensional array whose elements are the types of terrain, or None, which will be filled randomly. Array dimension must also be divisible by step.
setTemplate takes a template and returns True if successful.
import random,sys
class FractalMapGen:
def __init__(self, sample, getCallback, setCallback, dimension = (64,64), step = 2):
self.getTile = getCallback
self.setTile = setCallback
self.sample = sample
self.step = step
self.dimension = dimension
self.citer = 1
self.template = None
def setTemplate(self,template):
msx = len(template)
step = self.step
if msx % step:
print "ERROR: template dimension should be divisible by step."
print "Template does not apply"
return False
self.template = template
self.citer = int(msx/step)
citer = self.citer
sx,sy = self.dimension
for x in xrange(citer * step):
for y in xrange(citer * step):
x0 = int(sx/(citer * step)) * x
x1 = int(sx/(citer * step)) * (x + 1) - 1
y0 = int(sy/(citer * step)) * y
y1 = int(sy/(citer * step)) * (y + 1) - 1
if template[x][y] == None:
csample = self.sample
else:
csample = (template[x][y],)
self.fill(x0,y0,x1,y1,random.sample(csample,1)[0])
self.citer += 1
return True
def fill(self, x0, y0, x1, y1, val):
for x in xrange(x0,x1+1):
for y in xrange(y0,y1+1):
self.setTile((x,y),val)
def getSample(self, x0, y0, x1, y1):
sx,sy = self.dimension
left = x0 - 1 > 0
right = x1 + 1 < sx
up = y0 - 1 > 0
down = y1 + 1 < sy
sample = []
if left: sample.append(self.getTile((x0 - 1, y0)))
if right: sample.append(self.getTile((x1 + 1, y1)))
if up: sample.append(self.getTile((x0, y0 - 1)))
if down: sample.append(self.getTile((x1, y1 + 1)))
return sample
def generate(self, showProgress = 1, maxIters = 0):
random.seed()
citer = self.citer
step = self.step
sx,sy = self.dimension
if step < 2:
print 'ERROR: step should be > 1.'
return False
if sx % step or sy % step:
print 'ERROR: map dimensions should be divisible by step.'
print 'Generation aborted.'
return False
if maxIters > 0:
minIters = float(min(float(sx)/step + 1, float(sy)/step + 1, maxIters - 1))
elif maxIters < 0:
minIters = float(min(float(sx)/step + maxIters, float(sy)/step + maxIters))
if minIters < 1:
print 'ERROR: Incorrect maxIters value.'
print 'Generation aborted.'
return False
else:
minIters = min(float(sx)/step + 1, float(sy)/step + 1)
while (sx/(citer * step) >= 1) and (sy/(citer * step) >= 1):
for x in xrange(citer * step):
for y in xrange(citer * step):
x0 = int(sx/(citer * step)) * x
x1 = int(sx/(citer * step)) * (x + 1) - 1
y0 = int(sy/(citer * step)) * y
y1 = int(sy/(citer * step)) * (y + 1) - 1
if (citer > 1) or (self.template):
csample = self.getSample(x0,y0,x1,y1)
else:
csample = self.sample
self.fill(x0,y0,x1,y1,random.sample(csample,1)[0])
citer += 1
if showProgress == 1:
sys.stdout.write('Progress %.1f' % (int(citer/(minIters) * 100)) + '%\r')
sys.stdout.flush()
elif showProgress == 2:
percent = citer/(minIters)
p0 = int(percent * 10)
print '\033[0;0H'
print 'Progress |\033[41m%s\033[0m%s| %.1f' % (' ' * p0, ' ' * (10 - p0), int(percent * 100)) + '%'
print 'Iteration: %i' % citer
if maxIters and citer >= minIters:
return True
return True
Example 1: as a map image is used. Working with PIL (must be installed)
from PIL import Image
sx,sy = 128,128
colors = ((255,0,0),(0,255,0),(0,0,255),(250,150,50))
image = Image.new("RGB", (sx,sy), (0,0,0,0))
fg = FractalMapGen(colors, image.getpixel, image.putpixel, (sx,sy), 4)
fg.generate()
image.save("./test1.png", "PNG")
del image
Result:
Example 2: as the map uses an array of characters. Apply template
sx,sy = 24,24
tiles = ('_','#','^')
field = [[None for y in xrange(sy)] for x in xrange(sx)]
def getS(pos):
x,y = pos
return field[x][y]
def setS(pos, val):
x,y = pos
field[x][y] = val
tmp = (('_',None,None,'#'),
('^','_','#','^'),
('^','#','_','^'),
('#',None,None,'_'))
fg = FractalMapGen(tiles, getS, setS, (sx,sy), 4)
fg.setTemplate(tmp)
fg.generate()
for y in xrange(sy):
print ''
for x in xrange(sx):
print field[x][y],
Result:
_ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ # # # # # # # # # # #
_ ^ ^ ^ ^ ^ ^ ^ _ ^ ^ ^ # # # # # # # # # # # #
_ ^ ^ ^ ^ ^ _ _ _ ^ ^ ^ # # # # # # ^ ^ ^ ^ # #
^ ^ ^ ^ ^ ^ _ _ _ _ ^ ^ # # # # # # ^ ^ ^ ^ # #
^ _ _ _ _ ^ _ _ _ _ ^ ^ # # # # # # ^ ^ ^ ^ ^ ^
^ _ _ _ ^ ^ ^ _ _ _ ^ ^ # # # # # # # # ^ ^ ^ ^
_ _ _ ^ ^ ^ ^ _ _ ^ ^ ^ # # # # # # ^ # ^ ^ ^ ^
_ _ ^ _ _ _ ^ _ _ _ _ _ # _ _ _ ^ ^ _ # ^ ^ ^ ^
_ ^ ^ ^ ^ _ _ _ _ _ _ _ # # _ _ _ ^ _ ^ # ^ ^ #
^ ^ ^ ^ _ _ _ _ _ _ _ _ _ _ _ ^ _ ^ ^ # # # ^ #
^ ^ ^ ^ _ _ _ _ _ ^ _ _ _ _ _ ^ ^ ^ ^ ^ # # # #
^ ^ ^ ^ # _ ^ _ _ _ _ _ _ _ _ # ^ ^ ^ # # # # #
^ ^ ^ ^ # _ ^ ^ ^ ^ _ _ _ _ # # # _ _ _ # # # #
^ ^ ^ ^ # # # # ^ ^ ^ _ _ _ # # # _ # # # # # #
^ ^ ^ # # # # ^ # ^ ^ _ _ _ # # # # # # # # # #
^ ^ ^ ^ # # # # # ^ ^ ^ ^ _ # # # # # # _ # _ _
^ ^ ^ # # # # # ^ ^ ^ ^ ^ ^ # # # # # # _ # _ _
# # ^ # # # # # # ^ ^ ^ ^ ^ ^ ^ _ _ _ _ _ _ _ _
# # # # # # ^ ^ ^ ^ ^ # # # ^ ^ ^ # _ _ _ _ _ _
# # # # # # # ^ # ^ ^ # # ^ ^ # _ # _ _ _ _ _ _
# # # # # # # # # # # # # ^ # # # _ _ _ _ _ _ _
# # # # # # # # # # # ^ ^ ^ # _ _ _ _ _ _ _ _ _
# ^ ^ # # # # # # # # # ^ ^ # # _ _ _ _ _ _ _ _
# ^ ^ # # # # # # # # # # # # # _ _ _ _ _ _ _ _
a little tip - you can repeat the items in the “sample” tuple to adjust the ratio on the map:
tiles = (’’,’’,’#’,’#’,’#’,’^’)