#!BPY
# coding: utf-8
"""
Name: 'Maze'
Blender: 249a
Group: 'Add'
Tooltip: 'A maze generator'
"""
__author__ = 'Julian Habrock'
__version__ = '0.7 12/2009'
__email__ = ["jug:fantasymail*de"]
__bpydoc__ = """\
Maze Generator Script
Generate perfect maze meshes with individual sizes.
Perfect mazes are those with exactly one way between any two points.
"""
import Blender
from Blender import NMesh, Mesh
from Blender.BGL import *
from Blender.Draw import *
import math
from math import *
default = ""
selected = Blender.Object.GetSelected()
if selected:
for obj in selected:
if type(obj.data) in (Blender.Types.MeshType, Blender.Types.NMeshType):
default = obj.data.name
break
T_Object = Create(default)
T_Realtime = Create(True)
# Polygon Parameters
# width/height of the maze (2d)
T_CellRows = Create(5)
T_CellCols = Create(5)
# height [px] of the walls
T_Height = Create(0.7)
# width of cells
T_CellWidth = Create(0.5)
# width of walls, must be smaller than CellWidth
T_WallThickness = Create(50.0)
# Events
EVENT_NOEVENT = 1
EVENT_DRAW = 2
EVENT_EXIT = 3
EVENT_USE_SELECTED = 4
EVENT_REALTIME = 5
EVENT_HELP = 6
######################################################
# Draw GUI
######################################################
def draw():
global T_CellRows, T_CellCols, T_WallThickness, T_CellWidth, T_Height
global T_Object, T_Realtime
global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT
########## Title
glClear(GL_COLOR_BUFFER_BIT)
title = 'Maze Generator'
x = 300
str_w = GetStringWidth(title)
glRasterPos2d((220-str_w)/2, x)
Text(title)
######### Object Selector
x -= 30
T_Object = String("OB:", EVENT_NOEVENT, 10, x, 140, 18,
T_Object.val, 20, "Name of a mesh data object")
Button('auto',EVENT_USE_SELECTED, 160, x, 60, 18,
"Get name from selected Object")
x -= 30
T_Realtime = Toggle("Apply changes instantly", EVENT_REALTIME, 35,x,150,18,
T_Realtime.val,
"Regenerate maze whenever an option was changed")
# alternative to select a mesh object?!
# name = "OB: %t"
# objects = Blender.Object.Get()
# i = 1
# for obj in objects:
# if "Maze" in obj.getData(name_only=True):
# name += ("|%s %%x%i" % (obj.name, i))
# name += "|%l|create new"
# T_Object = Menu(name, EVENT_NOEVENT, 10, x, 100, 18, T_Object.val,
# "what object do you want to change?")
######### Options
x -= 30
glRasterPos2d(10, x)
Text("Parameters:")
x-= 35
T_CellRows = Number('Rows: ', EVENT_NOEVENT, 10, x, 210, 18,
T_CellRows.val, 2, 1000, 'Number of cell rows')
x -= 25
T_CellCols = Number('Columns: ', EVENT_NOEVENT, 10, x, 210, 18,
T_CellCols.val, 2, 1000, 'Number of cell columns')
####
x -= 25
T_Height = Number('Height: ', EVENT_NOEVENT, 10, x, 210, 18,
T_Height.val, 0, 100, 'Height of the walls')
x -= 25
T_CellWidth = Number('Cell Width: ', EVENT_NOEVENT, 10, x, 210, 18,
T_CellWidth.val, 0, 100, 'Size of one cell')
####
x -= 35
T_WallThickness = Slider('Wall Size: ', EVENT_NOEVENT, 10, x, 210, 18,
T_WallThickness.val, 0, 100, 1, 'Amount of wall per cell');
######### Action Buttons
# Button('Generate',EVENT_DRAW , 10, 10, 60, 18)
# Button('Help',EVENT_HELP , 85, 10, 60, 18)
# Button('Exit',EVENT_EXIT , 160, 10, 60, 18)
Button('Generate',EVENT_DRAW , 30, 10, 80, 18)
Button('Exit',EVENT_EXIT , 120, 10, 80, 18)
def event(evt, val):
"""
handle key events
"""
if (evt == QKEY and not val) or evt == Blender.Draw.ESCKEY:
Exit()
def bevent(evt):
"""
handle custom events
"""
global T_Object
global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT
if (evt == EVENT_EXIT):
Exit()
elif (evt== EVENT_DRAW) or (T_Realtime.val and evt== EVENT_NOEVENT):
make_maze(T_CellCols.val, T_CellRows.val, T_CellWidth.val,
T_Height.val,
T_WallThickness.val*T_CellWidth.val/100., T_Object.val)
#! This does not work. But why?
# elif (evt == EVENT_HELP):
# Blender.ShowHelp("maze.py")
elif (evt == EVENT_USE_SELECTED):
selected = Blender.Object.GetSelected()
if selected:
for obj in selected:
if type(obj.data) in (Blender.Types.MeshType,
Blender.Types.NMeshType):
T_Object.val = obj.data.name
Blender.Redraw()
break
Register(draw, event, bevent)
######################################################
# Maze Logic
######################################################
import random
class Cell(object):
"""
One cell in a maze
"""
def __init__(self, maze, i):
"""
:type maze: :class:`Maze`
:param maze: reference to the Maze object
:type i: int
:param i: cell index in maze
"""
#: reference to the maze
self.maze = maze
#: index in maze
self.index = i
#: used for backtracing
self.visited = False
#: col and row of the cell
self.position = i % maze.width, i / maze.width
def get_neighbours(self):
"""
:rtype: list
:return: Top, right, bottom and left neighbour cells or None.
Eg. for the cell at (0,0) it will return
``[None, , | , None]``\ .
"""
n = []
for i in (self.index-self.maze.width, self.index+1,
self.index+self.maze.width, self.index-1):
# need to validate positions in case of a edge cell that has not
# neighbours at all 4 possible positions
if 0<=i" % ((self.index,)+self.position)
class Maze(object):
"""
A perfect Maze that generates and solves itself.
"""
def __init__(self, width, height):
"""
Create the basic structure and attributes. This does not yet generate
an actual maze.
"""
self.width, self.height = width, height
#: list of Cell objects from topleft to downright
self.cells = [Cell(self, i) for i in xrange(self.width * self.height)]
#: dict of *walls* with ordered cell pairs as keys and booleans (that
#: indicate if there is a wall) as values
self.walls = {}
# create all possible walls
for cell in self.cells:
right, bottom = cell.get_neighbours()[1:3]
if right:
self.walls[(cell, right)] = True
if bottom:
self.walls[(cell, bottom)] = True
print "generated %i cells and %i walls" % (len(self.cells), len(self.walls))
def reset_walls(self):
"""
recreate all possible walls
"""
for k in self.walls:
self.walls[k] = True
def reset_cells(self):
"""
reset all Cells to *not visited*
"""
for cell in self.cells:
cell.visited = False
def generate(self):
"""
generate the maze
"""
i = 0
stack = [self.cells[random.randint(0,len(self.cells)-1)]]
while stack:
cell = stack[-1]
cell.visited = True
nc = cell.get_free_neighbour()
if nc:
stack.append(nc)
self.walls[tuple(sorted((cell, nc)))] = False
else:
stack.pop()
i += 1
print "generated in %i steps" % i
# count walls
walls, gaps = 0,0
for v in self.walls.itervalues():
if v:
walls += 1
else:
gaps += 1
self.reset_cells()
print "%i connections: %i walls and %i gaps" % (walls+gaps, walls, gaps)
def search_way(self, start, end):
"""
:type start: :class:`Cell`
:param start: Cell to start the search at.
:type end: :class:`Cell`
:param end: Cell to search for.
:rtype: list
:return: Cells on the calculated path.
Find a way from one cell to another.
"""
stack = [start]
while stack:
cell = stack[-1]
cell.visited = True
if cell == end:
break
nc = cell.get_free_neighbour(ignore_walls=False)
if nc:
stack.append(nc)
else:
stack.pop()
if not stack:
print "No way"
else:
print "Found way: %i steps" % len(stack)
self.reset_cells()
return stack
def __len__(self):
"""
:rtype: int
:return: number of cells
"""
self.width * self.height
assert self.width * self.height == len(self.cells)
return self.width * self.height
######################################################
# Create Mesh from Maze Data
######################################################
def make_maze(w, h, cell_width, wall_height, wall_thickness, name):
# x/y: col/row
# add: absolute pixel amount to add
fx = lambda x, add: x*cell_width+add -(w*cell_width/2.)
fy = lambda y, add: y*cell_width+add -(h*cell_width/2.)
#fy = lambda y, add: ((h/2.)-y) * cell_width
maze = Maze(w, h)
maze.generate()
poly = NMesh.GetRaw()
# x, y, anchor -> vert
verts = {}
# vert -> vert
u_verts = {}
for x in xrange(w+1):
for y in xrange(h+1):
i = 1
f = NMesh.Face()
poly.faces.append(f)
for x_add, y_add in ((1,-1), (1, 1), (-1, 1), (-1, -1)):
v = NMesh.Vert(fx(x, x_add * wall_thickness/2.),
fy(y, y_add * wall_thickness/2.), 0)
poly.verts.append(v)
verts[x, y, i] = v
v2 = NMesh.Vert(v.co[0], v.co[1], v.co[2] + wall_height)
poly.verts.append(v2)
u_verts[v] = v2
f.append(v2)
i += 1
# border faces
faces = []
for x in xrange(w):
for i in (0, h):
v1, v2 = verts[x,i, 2], verts[x+1, i, 3]
v3, v4 = verts[x,i, 1], verts[x+1, i, 4]
faces += [(v1, v2, u_verts[v2], u_verts[v1]),
(u_verts[v1], u_verts[v2], u_verts[v4], u_verts[v3]),
(v3, v4, u_verts[v4], u_verts[v3])]
if i == 0:
v5 = verts[x,i, 4]
faces.append((v3, v5, u_verts[v5], u_verts[v3]))
else:
v5 = verts[x,i, 3]
faces.append((v1, v5, u_verts[v5], u_verts[v1]))
###
for y in xrange(h):
for i in (0, w):
v1, v2 = verts[i, y, 2], verts[i, y+1, 1]
v3, v4 = verts[i, y, 3], verts[i, y+1, 4]
faces += [(v1, v2, u_verts[v2], u_verts[v1]),
(u_verts[v1], u_verts[v2], u_verts[v4], u_verts[v3]),
(v3, v4, u_verts[v4], u_verts[v3])]
if i == 0:
v5 = verts[i, y, 4]
faces.append((v3, v5, u_verts[v5], u_verts[v3]))
else:
v5 = verts[i, y, 1]
faces.append((v1, v5, u_verts[v5], u_verts[v1]))
###
# last corners
# -> x
v1, v2 = verts[w, 0, 1], verts[w, 0, 4]
v3, v4 = (verts[w, h, 3],
verts[w, h, 2])
faces += [(v1, v2, u_verts[v2], u_verts[v1]),
(v3, v4, u_verts[v4], u_verts[v3])]
# -> y
v1, v2 = verts[0, h, 3], verts[0, h, 4]
v3, v4 = (verts[w, h, 1],
verts[w, h, 2])
faces += [(v1, v2, u_verts[v2], u_verts[v1]),
(v3, v4, u_verts[v4], u_verts[v3])]
# create faces
for face in faces:
f = NMesh.Face()
for vert in face:
f.append(vert)
poly.faces.append(f)
# actual maze
for wall, exists in maze.walls.iteritems():
cell1, cell2 = wall
cell1x, cell1y = cell1.position
cell2x, cell2y = cell2.position
if cell1x == cell2x:
vc1a = verts[cell1x, cell2y, 1]
vc1b = verts[cell1x, cell2y, 2]
vc2a = verts[cell1x+1, cell2y, 4]
vc2b = verts[cell1x+1, cell2y, 3]
elif cell1y == cell2y:
vc1a = verts[cell2x, cell2y, 3]
vc1b = verts[cell2x, cell2y, 2]
vc2a = verts[cell2x, cell2y+1, 4]
vc2b = verts[cell2x, cell2y+1, 1]
else:
print "cell %s and cell %s are not neighbours" % (cell1, cell2)
continue
if exists:
# make wall
faces = [(vc1a, vc2a, u_verts[vc2a], u_verts[vc1a]),
(vc1b, vc2b, u_verts[vc2b], u_verts[vc1b]),
(u_verts[vc2a], u_verts[vc2b],
u_verts[vc1b], u_verts[vc1a])
]
else:
# make passage
faces = [(vc1a, vc1b, u_verts[vc1b], u_verts[vc1a]),
(vc2a, vc2b, u_verts[vc2b], u_verts[vc2a])]
# make faces
for face in faces:
f = NMesh.Face()
for vert in face:
f.append(vert)
poly.faces.append(f)
if name in NMesh.GetNames():
mesh = Mesh.Get(name)
mat = mesh.materials or []
else:
mesh = None
mat = []
NMesh.PutRaw(poly, name)
if mesh:
mesh.materials = mat
Blender.Redraw()
| |