#!BPY

# """
# Name: 'Auto Masonry'
# Blender: 248
# Group: 'Wizards'
# Tooltip: 'Builds a masonry wall, path, etc.'
# """

__author__ = 'Paul Spooner, aka "Dudecon" or "Ziggy"'
__version__ = '0.1 2007/09/14'
__url__ = ["Author's site, http://www.peripheralarbor.com"]
__email__ = ["Author's personal e-mail, dudecon:hotmail*com"]
__bpydoc__ = """\
This script builds a wall.<br>
<br>
Actually, it could be a path, or a tower, or anything made with what looks like bricks or stones.
Arches are not supported yet, but hopefully they will be with the next version.
If a curve is the active object the wall will follow the curve (as long as a closed curve is a circle, 
let me know if you can find a work-around for this).  In this way, castles or large citys can be quickly
made by creating curve circles for the towers and curve segments for the walls.  Crenelations also work
which can double as arrow slits or support beam holes.  Let me know about suggested improvements,
I'll try to work them in!
"""
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) Paul Spooner, aka "Dudecon" or "Ziggy")
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
# Version History
# V0.1 2007/09/14		First release!

import Blender, time, math, bpy
from Blender.Mathutils import Vector
from math import fmod

SMALL = 0.000000000001
PI = math.pi

MaxW = Blender.Draw.Create(3.2)		# maximum brick width, larger than MinW
MinW = Blender.Draw.Create(1.7)		# minumum brick width (not the absolute minimum), larger than 2*GroutWidth + 2*Bevel
MaxH = Blender.Draw.Create(1.0)		# maximum brick height, larger than MinH
MinH = Blender.Draw.Create(1.5)		# minumum brick height (not the absolute minimum), larger than 2*GroutWidth + 2*Bevel
MaxD = Blender.Draw.Create(1.5)		# maximum brick depth larger than MinD
MinD = Blender.Draw.Create(1.3)		# minimum brick depth (not the absolute minimum), larger than 2*GroutWidth + 2*Bevel
Taper = Blender.Draw.Create(0.0)	# fraction of taper at top, can be less than zero or greater than one
Width = Blender.Draw.Create(40.0)	# total width of the wall, larger than 2*GroutWidth + 2*Bevel
GroutWidth = Blender.Draw.Create(0.080)	# half the width of the grout between the bricks
GroutDepth = Blender.Draw.Create(.100)	# depth back from the min face position
Height = Blender.Draw.Create(20.0)	# Total height of the wall (minus the crenels), larger than 2*GroutWidth + 2*Bevel
Bevel = Blender.Draw.Create(0.100)	# the amount to bevel the edge of the bricks
HWeight = Blender.Draw.Create(0.5)	# how much the height of the row affects the width of the bricks in that row, recommended between -4 and 4, 0.5 is a good value
#FlawFraction = 0			# between 0 and 1, the fraction of verticies recieving the first bevel operation
#FlawSize = 0				# the size of the first bevel operation less than half of (MinH or MinW) - Bevel
OpeningType = Blender.Draw.Create(1)	# 0 is straight, 1 is angled windows, 2 is angled doors, 3 is both
EdgeType = Blender.Draw.Create(1)	# 1 is straight edges, ? is offset left edges, ? is offset right edges(doesn't work), 2 is both
DoorWidth = Blender.Draw.Create(4.)
WindowWidth = Blender.Draw.Create(.2)
CrenelWidth = Blender.Draw.Create(5.5)
DoorHeight = Blender.Draw.Create(7.0)
WindowHeight = Blender.Draw.Create(4.)
CrenelHeight = Blender.Draw.Create(5.5*0.618)
DoorSpacing = Blender.Draw.Create(20.0)
WindowSpacing = Blender.Draw.Create(10.0)
CrenelSpacing = Blender.Draw.Create(6.5)
DoorActive = Blender.Draw.Create(1)
WindowActive = Blender.Draw.Create(1)
CrenelActive = Blender.Draw.Create(0)
DoorAngle = Blender.Draw.Create(0)
WindowAngle = Blender.Draw.Create(1)




		#easier way to get to the random function
def rnd(): return Blender.Mathutils.Rand()


def LinearFill(left, right, avedst, mindst, dev=0.0, pad=(0.0,0.0), num=0, center=0):
	__doc__ = '''\
	LinearFill fills a linear range with points and returns an ordered list of those points
	including the end points.
	left: the lower boundary
	right: the upper boundary
	avedst: the average distance between points
	mindst: the minimum distance between points
	dev: the maximum random deviation from avedst
	pad: tends to move the points near the bounds right (positive) or left (negative).
		element 0 pads the lower bounds, element 1 pads the upper bounds
	num: substitutes a numerical limit for the right limit.  LinearFill will then make
		a num+1 element list
	center: flag to center the elements in the range, 0 == disabled
	'''
	
	poslist = [left]
	curpos = left+pad[0]
	if center:
		curpos += ((right-left-mindst*2)%avedst)/2+mindst
		if curpos-poslist[-1]<mindst: curpos = poslist[-1]+mindst+rnd()*dev/2
		if (right-curpos<mindst) or (right-curpos< mindst-pad[1]):
			poslist += [right]
			return poslist
		else: poslist += [curpos]
		
	if num:
		idx = len(poslist)
		while idx<num+1:
			curpos += avedst+(rnd()-0.5)*dev*2
			if curpos-poslist[-1]<mindst: curpos = poslist[-1]+mindst+rnd()*dev/2
			poslist += [curpos]
			idx += 1


	else:
		while True: # loop for blocks	
			curpos += avedst+(rnd()-0.5)*dev*2			
			if curpos-poslist[-1]<mindst: curpos = poslist[-1]+mindst+rnd()*dev/2
			if (right-curpos<mindst) or (right-curpos< mindst-pad[1]):
				poslist += [right]
				return poslist
			else: poslist += [curpos]



def MakeABlock(bounds, segsize, grout=0, vll=0, Offsets=None, FaceExclude=[], bevel=0):
	__doc__ = '''\
	MakeABlock returns lists of points and faces to be made into a square
		cornered block, subdivided along the length.
	bounds: a list of boundary positions, 0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
	segsize: the maximum size before lengthwise subdivision occurs
	grout: the width of the edge offset, like a picture frame, larger values result
		in a smaller block.  Only affects the y and z size
	vll: the number of vertexes already in the mesh. len(mesh.verts) should give this number
	Offsets: list of coordinate delta values.
		Offsets are lists, [x,y,z] in
			[
			0:left_bottom_back,
			1:left_bottom_front,
			2:left_top_back,
			3:left_top_front,
			4:right_bottom_back,
			5:right_bottom_front,
			6:right_top_back,
			7:right_top_front,
			]
	faceExclude: list of faces to exclude from the faces list.  see bounds above for indicies
	'''

	if Offsets == None: Offsets = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
	if bevel:
		slices = LinearFill(bounds[0]+bevel+grout, bounds[1]-bevel-grout, segsize, grout+segsize, center=1)
		slices.insert(0,bounds[0])
		slices.append(bounds[1])
	else: slices = LinearFill(bounds[0], bounds[1], segsize, grout+segsize, center=1)
	points = []
	faces = []
	faceinclude = [1 for x in range(6)]
	for x in FaceExclude: faceinclude[x]=0

	if (not bevel) or faceinclude[0]:
		points.append([slices[0]+grout+Offsets[0][0],bounds[4]+Offsets[0][1]+bevel,bounds[2]+grout+Offsets[0][2]+bevel])
		points.append([slices[0]+grout+Offsets[1][0],bounds[5]+Offsets[1][1]-bevel,bounds[2]+grout+Offsets[1][2]+bevel])
		points.append([slices[0]+grout+Offsets[3][0],bounds[5]+Offsets[3][1]-bevel,bounds[3]-grout+Offsets[3][2]-bevel])
		points.append([slices[0]+grout+Offsets[2][0],bounds[4]+Offsets[2][1]+bevel,bounds[3]-grout+Offsets[2][2]-bevel])
	
	if bevel:
		for x in slices[1:-1]:
			xwt = (x-bounds[0])/(bounds[1]-bounds[0])
			points.append([x+Offsets[0][0]*(1-xwt)+Offsets[4][0]*xwt, bounds[4]+Offsets[0][1]*(1-xwt)+Offsets[4][1]*xwt, bounds[2]+grout+Offsets[0][2]*(1-xwt)+Offsets[4][2]*xwt+bevel])
			points.append([x+Offsets[0][0]*(1-xwt)+Offsets[4][0]*xwt, bounds[4]+Offsets[0][1]*(1-xwt)+Offsets[4][1]*xwt+bevel, bounds[2]+grout+Offsets[0][2]*(1-xwt)+Offsets[4][2]*xwt])
			points.append([x+Offsets[1][0]*(1-xwt)+Offsets[5][0]*xwt, bounds[5]+Offsets[1][1]*(1-xwt)+Offsets[5][1]*xwt-bevel, bounds[2]+grout+Offsets[1][2]*(1-xwt)+Offsets[5][2]*xwt])
			points.append([x+Offsets[1][0]*(1-xwt)+Offsets[5][0]*xwt, bounds[5]+Offsets[1][1]*(1-xwt)+Offsets[5][1]*xwt, bounds[2]+grout+Offsets[1][2]*(1-xwt)+Offsets[5][2]*xwt+bevel])
			points.append([x+Offsets[3][0]*(1-xwt)+Offsets[7][0]*xwt, bounds[5]+Offsets[3][1]*(1-xwt)+Offsets[7][1]*xwt, bounds[3]-grout+Offsets[3][2]*(1-xwt)+Offsets[7][2]*xwt-bevel])
			points.append([x+Offsets[3][0]*(1-xwt)+Offsets[7][0]*xwt, bounds[5]+Offsets[3][1]*(1-xwt)+Offsets[7][1]*xwt-bevel, bounds[3]-grout+Offsets[3][2]*(1-xwt)+Offsets[7][2]*xwt])
			points.append([x+Offsets[2][0]*(1-xwt)+Offsets[6][0]*xwt, bounds[4]+Offsets[2][1]*(1-xwt)+Offsets[6][1]*xwt+bevel, bounds[3]-grout+Offsets[2][2]*(1-xwt)+Offsets[6][2]*xwt])
			points.append([x+Offsets[2][0]*(1-xwt)+Offsets[6][0]*xwt, bounds[4]+Offsets[2][1]*(1-xwt)+Offsets[6][1]*xwt, bounds[3]-grout+Offsets[2][2]*(1-xwt)+Offsets[6][2]*xwt-bevel])

	else:
		for x in slices[1:-1]:
			xwt = (x-bounds[0])/bounds[1]
			points.append([x+Offsets[0][0]*(1-xwt)+Offsets[4][0]*xwt,bounds[4]+Offsets[0][1]*(1-xwt)+Offsets[4][1]*xwt,bounds[2]+grout+Offsets[0][2]*(1-xwt)+Offsets[4][2]*xwt])
			points.append([x+Offsets[1][0]*(1-xwt)+Offsets[5][0]*xwt,bounds[5]+Offsets[1][1]*(1-xwt)+Offsets[5][1]*xwt,bounds[2]+grout+Offsets[1][2]*(1-xwt)+Offsets[5][2]*xwt])
			points.append([x+Offsets[3][0]*(1-xwt)+Offsets[7][0]*xwt,bounds[5]+Offsets[3][1]*(1-xwt)+Offsets[7][1]*xwt,bounds[3]-grout+Offsets[3][2]*(1-xwt)+Offsets[7][2]*xwt])
			points.append([x+Offsets[2][0]*(1-xwt)+Offsets[6][0]*xwt,bounds[4]+Offsets[2][1]*(1-xwt)+Offsets[6][1]*xwt,bounds[3]-grout+Offsets[2][2]*(1-xwt)+Offsets[6][2]*xwt])

	
	if not bevel or faceinclude[1]:
		points.append([slices[-1]-grout+Offsets[4][0],bounds[4]+Offsets[4][1]+bevel,bounds[2]+grout+Offsets[4][2]+bevel])
		points.append([slices[-1]-grout+Offsets[5][0],bounds[5]+Offsets[5][1]-bevel,bounds[2]+grout+Offsets[5][2]+bevel])
		points.append([slices[-1]-grout+Offsets[7][0],bounds[5]+Offsets[7][1]-bevel,bounds[3]-grout+Offsets[7][2]-bevel])
		points.append([slices[-1]-grout+Offsets[6][0],bounds[4]+Offsets[6][1]+bevel,bounds[3]-grout+Offsets[6][2]-bevel])

	if faceinclude[0]: faces.append([vll,vll+3,vll+2,vll+1])
	
	if bevel:
		if faceinclude[0]:
			if faceinclude[4] and faceinclude[2]: faces.append([vll,vll+5,vll+4])
			if faceinclude[2]: faces.append([vll,vll+1,vll+6,vll+5])
			if faceinclude[2] and faceinclude[5]: faces.append([vll+1,vll+7,vll+6])
			if faceinclude[5]: faces.append([vll+1,vll+2,vll+8,vll+7])
			if faceinclude[5] and faceinclude[3]: faces.append([vll+2,vll+9,vll+8])
			if faceinclude[3]: faces.append([vll+2,vll+3,vll+10,vll+9])
			if faceinclude[3] and faceinclude[4]: faces.append([vll+3,vll+11,vll+10])
			if faceinclude[4]: faces.append([vll+3,vll,vll+4,vll+11])
			vll+=4
		
		for x in range(len(slices)-3):
			if faceinclude[4] and faceinclude[2]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[2]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[2] and faceinclude[5]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[5]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[5] and faceinclude[3]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[3]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[3] and faceinclude[4]: faces.append([vll,vll+1,vll+9,vll+8])
			vll+=1
			if faceinclude[4]: faces.append([vll,vll-7,vll+1,vll+8])
			vll+=1
		
		if faceinclude[1]:
			vll+=8
			if faceinclude[4] and faceinclude[2]: faces.append([vll,vll-8,vll-7])
			if faceinclude[2]: faces.append([vll,vll-7,vll-6,vll+1])
			if faceinclude[2] and faceinclude[5]: faces.append([vll+1,vll-6,vll-5])
			if faceinclude[5]: faces.append([vll+1,vll-5,vll-4,vll+2])
			if faceinclude[5] and faceinclude[3]: faces.append([vll+2,vll-4,vll-3])
			if faceinclude[3]: faces.append([vll+2,vll-3,vll-2,vll+3])
			if faceinclude[3] and faceinclude[4]: faces.append([vll+3,vll-2,vll-1])
			if faceinclude[4]: faces.append([vll+3,vll-1,vll-8,vll])
	
	
	else:
		for x in range(len(slices)-1):
			if faceinclude[2]: faces.append([vll,vll+1,vll+5,vll+4])
			vll+=1
			if faceinclude[5]: faces.append([vll,vll+1,vll+5,vll+4])
			vll+=1
			if faceinclude[3]: faces.append([vll,vll+1,vll+5,vll+4])
			vll+=1
			if faceinclude[4]: faces.append([vll,vll-3,vll+1,vll+4])
			vll+=1
	
	if faceinclude[1]: faces.append([vll,vll+1,vll+2,vll+3])
	return points, faces


def RowsMaker((MaxW, MinW, MaxH, MinH, MaxD, MinD, GroutWidth, GroutDepth, Bevel, HWeight), Taper, Width, Height, OverW, OverH, Offset, oldposz, EdgTyp, Edgedata=None):
	__doc__ = '''\
	RowsMaker creates points and faces for a generally rectangular wall section.
		It returns a list of points and faces for the blocks and grout, the
		total number of vertical rows so far, and the Edgedata variable.
	Settings: see the body of the main function below
	Taper: how much to taper the wall with increasing height, 0.0 is no taper, 1.0 is
		taper to a point, values may be less than zero or greater than one
		value is a list [start, end] for height
	Width: general width of the wall section
	Height: general height of the wall section
	OverW: override the bricks, make one block over the entire width
	OverH: override the rows, make one row over the entire height
	Offset: see the documentation for MakeABlock above
	oldposz: the total number of horizontal rows preceeding this call, used to
		keep the edge offset consistent
	EdgTyp: the type of edge: 0 is straight, 1 is jagged left edge, write to 
		Edgedata, 2 is jagged right edge, read from Edgedata, 3 is match
		both edges, and write to Edgedata
	Edgedata: a list of two element lists, element 0 is the vertical position of
		the row, element 1 is the horizontal offset of that edge
	'''

	bounds = [0.0,0.0,0.0,0.0,0.0,0.0] #0:left, 1:right, 2:bottom, 3:top, 4:back, 5:front
	depth = 0.0
	offsetapply = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
	OffsetdistL = [abs(x[0]) for x in Offset[:4]]
	OffsetdistR = [abs(x[0]) for x in Offset[-4:]]
	OffsetdistL.sort()
	OffsetdistR.sort()
	OffsetMaxL = OffsetdistL[-1]
	OffsetMaxR = OffsetdistR[-1]
	groutwalls = [0,1]
	blockwalls = []
	blockverts = []
	blockfcs = []
	groutverts = []
	groutfcs = []

	if OverH: HL = [0.0,Height]			#Initialize Height List
	elif EdgTyp==2: HL = [x[0] for x in Edgedata]
	else: HL = LinearFill(0.0,Height,(MaxH+MinH)/2,GroutWidth*2+Bevel*2,(MaxH-MinH)/2)
	if fmod(EdgTyp,2)==1: Edgedata = [[x,0] for x in HL]
	WLlist = []
	if OverW: WLlist = [[0.0,Width] for x in range(len(HL)-1)]
	else:
		for posz in range(len(HL)-1):		#Initialize Width List
			hwt = (((HL[posz+1]-HL[posz])*2/(MinH+MaxH)-1)*HWeight+1)
			pad = (-fmod(posz+oldposz,2))*(MaxW+MinW)/4 #+rnd()*(MaxW-MinW)/2
			if fmod(EdgTyp,2) == 1:
				left=Edgedata[posz][1]=-pad
				pad = 0
			else: left = 0
			if EdgTyp > 1 : right=Edgedata[posz][1]
			else: right = 0
			WL = LinearFill(OffsetMaxL+left,Width-OffsetMaxR+right,hwt*(MaxW+MinW)/2,GroutWidth*2+Bevel*2,hwt*(MaxW-MinW)/2,(pad,pad))
			WL[0] = left
			WL[-1] = Width+right
			WLlist += [WL[:]]
	
	
	for posz in range(len(HL)-1): # loop for rows
		bounds[2]=HL[posz]	#set the top and bottom bounds
		bounds[3]=HL[posz+1]
		
		WL = WLlist[posz]
		for posx in range(len(WL)-1): # loop for blocks
			bounds[0] = WL[posx] # set the right and left bounds
			bounds[1] = WL[posx+1]
			if bounds[0] == 0: offsetapply[:4] = Offset[:4]
			if bounds[1] == Width: offsetapply[-4:] = Offset[-4:]
			if (Width<OffsetMaxL+OffsetMaxR+GroutWidth*2+Bevel*2) and (OffsetMaxL+OffsetMaxR):
				offsetapply = [[x[0]*(Width-GroutWidth*2-Bevel*2)/(OffsetMaxL+OffsetMaxR),x[1],x[2]] for x in offsetapply]
			
			# set the brick depth
			depthback = -(1-Taper*bounds[3]/Height)*(MinD + rnd()*(MaxD - MinD))/2
			depthfront = (1-Taper*bounds[3]/Height)*(MinD + rnd()*(MaxD - MinD))/2
			if abs(depthfront-depthback) < Bevel*2:
				depthback = -Bevel
				depthfront = Bevel
			bounds[4] = depthback
			bounds[5] = depthfront
			
			# make the block vertex and face data
			block = MakeABlock(bounds,(MaxW+MinW)/2,GroutWidth,len(blockverts), offsetapply, blockwalls, bevel=Bevel)
			
			blockverts+=block[0]
			blockfcs+=block[1]
			
			# reset the bounds for the grout
			bounds[4] = GroutDepth-(1-Taper*bounds[3]/Height)*MinD/2
			bounds[5] = (1-Taper*bounds[3]/Height)*MinD/2-GroutDepth
			offsetapply = [[x[0],0,x[2]] for x in offsetapply]
			
			# move the grout in on the edges of beveled surfaces and fill the edges.
			if (bounds[0]==0) and OffsetMaxL:
				offsetapply[:4] = [[x[0]+GroutWidth+Bevel*3,x[1],x[2]] for x in offsetapply[:4]]
				groutwalls.remove(0)
			if bounds[1]==Width and OffsetMaxR:
				offsetapply[-4:] = [[x[0]-GroutWidth-Bevel*3,x[1],x[2]] for x in offsetapply[-4:]]
				groutwalls.remove(1)
			
			# move the grout edges in if there is a bevel, and the blocks are narrow
			if (Width<OffsetMaxL+OffsetMaxR+GroutWidth*4+Bevel*5) and (OffsetMaxL+OffsetMaxR):
				for x in (0,2):
					offsetapply[x] = [offsetapply[x][0]-GroutWidth-Bevel*1.5,offsetapply[x][1],offsetapply[x][2]]

				for x in (4,6):
					offsetapply[x] = [offsetapply[x][0]+GroutWidth+Bevel*1.5,offsetapply[x][1],offsetapply[x][2]]
			
			
			grout = MakeABlock(bounds,(MaxW+MinW)/2,0,len(groutverts), offsetapply, groutwalls)
			groutverts+=grout[0]
			groutfcs+=grout[1]
			offsetapply = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
			if 0 not in groutwalls: groutwalls.append(0)
			if 1 not in groutwalls: groutwalls.append(1)
		

	return blockverts, blockfcs, groutverts, groutfcs, len(HL)-1, Edgedata



def WallGen (Settings, Spec, FlawFraction=0, FlawSize=0):
	__doc__ = '''\
	WallGen creates wall and grout mesh objects in the current scene, aligned
	with the x axis.
	Settings: see the body of the main function below
	Spec: a tuple of specifications for the wall
		Spec values are
		(
		0:offset X,
		1:offset Z,
		2:Size X,
		3:Size Z,
		4:Single block X,
		5:Single block Z
		6:Offset list (list of three element lists, [x,y,z] for all corners)
		7:Edge setting: (0 is normal (straight), 1 is read(left), 2 is write(right), 3 is both (loop))
		8:[Taper start, Taper end]
		)
	FlawFraction: the fraction of corners to create flaws on.  This is essentially an extra beveling step
	FlawSize: the size of the flaws, or the extra bevel
	'''
	
	scn = Blender.Scene.GetCurrent()
	oldposz = 0
	Edgedata = []
	wallmesh = []
	groutmesh = []
	wallob = []
	groutob = []

	
	for idx, CurSpec in enumerate(Spec):
		if CurSpec[2]<=0 or CurSpec[3]<=0:continue
		bvts, bfcs, gvts, gfcs, posz, Edgedata= RowsMaker(Settings, CurSpec[8], CurSpec[2], CurSpec[3], CurSpec[4], CurSpec[5], CurSpec[6], oldposz, CurSpec[7], Edgedata)
		if CurSpec[0]==0:
			oldposz += posz
		
		wallmesh+=[Blender.Mesh.New('wall')]
		wallmesh[-1].verts.extend(bvts)
		wallmesh[-1].faces.extend(bfcs)
		wallob += [scn.objects.new(wallmesh[-1], 'wall')]
		wallob[-1].LocX = CurSpec[0]
		wallob[-1].LocZ = CurSpec[1]
		for edge in wallmesh[-1].edges: edge.crease = 127
		
		groutmesh+=[Blender.Mesh.New('grout')]
		groutmesh[-1].verts.extend(gvts)
		groutmesh[-1].faces.extend(gfcs)
		groutob += [scn.objects.new(groutmesh[-1], 'grout')]
		groutob[-1].LocX = CurSpec[0]
		groutob[-1].LocZ = CurSpec[1]
	
	
	wall = wallob.pop(0)
	grout = groutob.pop(0)
	wall.join(wallob)
	grout.join(groutob)
	for objct in wallob: scn.objects.unlink(objct)
	for objct in groutob: scn.objects.unlink(objct)
	for msh in wallmesh[1:]: msh.verts.delete(range(len(msh.verts)))
	for msh in groutmesh[1:]: msh.verts.delete(range(len(msh.verts)))
	return wall, grout


def Spec_Generator(Settings, Width, Height, Taper=0, OpeningType=0, EdgeType=0, Doors=None, Windows=None, Crenel=None):
	__doc__ = '''\
	Spec_Generator creates the specifications for a wall with doors, windows, and crenelations.
		it returns a tuple, see the description of WallGen above for details.
	Settings: see the body of the main function below
	Width: total width of the wall
	Height: total height of the wall
	OpeningType: 0 is straight edge openings, 1 is angled windows, 2 is angled doors, 3 is both
	EdgeType: controlls the overall left and right edges, 0 is straight edges, 1 is offset
		left edges, 2 is offset right edges (doesn't work right now), 3 is both
	Doors: a list, element 0 is width, element 1 is height, the rest are center positions
	Windows: same as Doors, but for windows
	Crenel: same as Doors, but for crenelation
	'''
	
	ave = (Settings[2]+Settings[3])/2
	bev = ave*2/3
	groutbk = -Settings[6]-Settings[7]
	Offsets = [[bev,0,0],[0,0,0],[bev,0,0],[0,0,0],[-bev,0,0],[0,0,0],[-bev,0,0],[0,0,0]]
	OffsetsNone = [[SMALL,0,0],[SMALL,0,0],[SMALL,0,0],[SMALL,0,0],[-SMALL,0,0],[-SMALL,0,0],[-SMALL,0,0],[-SMALL,0,0]]
	TopOffsets = [[0,0,0],[0,0,0],[0,0,groutbk],[0,0,groutbk],[0,0,0],[0,0,0],[0,0,groutbk],[0,0,groutbk]]
	LintelOffsets = [[0,0,0],[0,.3,0],[0,0,0],[0,.2,0],[0,0,0],[0,.3,0],[0,0,0],[0,.2,0]]
	offsetapply = [[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
	edgeapply = 0
	midsegs = []
	lowsegs = []
	crensegs = []
	speclist = []
	if Doors != None or Windows != None:
		DoorHeight = Doors[1]
		if Doors[2] != None:
			for x in Doors[2:]:
				midsegs.append([x-Doors[0]/2,x+Doors[0]/2,1,2])
	
	else: DoorHeight = 0


	lowsegs = midsegs[:]
	if Windows != None:
		WindowHeight = Windows[1]
		for x in Windows[2:]:
			midsegs.append([x-Windows[0]/2,x+Windows[0]/2,1,1])
	
	else: WindowHeight = 0

	if Crenel != None:
		for x in Crenel[2:]:
			crensegs.append([x-Crenel[0]/2,x+Crenel[0]/2])

	midsegs.sort()
	lowsegs.sort()
	midsegs.insert(0,[0,0,0,0])
	lowsegs.insert(0,[0,0,0,0])
	midsegs+=[[Width,Width,0,0]]
	lowsegs+=[[Width,Width,0,0]]
	
	#Spaces between the doors at the bottom
	for x in range(len(lowsegs)-1):
		if lowsegs[x+1][0]-lowsegs[x][1]>0:
			if lowsegs[x][3]==2:
				if (OpeningType==2 or OpeningType==3): offsetapply[:4] = Offsets[:4]
				else: offsetapply[:4] = OffsetsNone[:4]
			if lowsegs[x+1][3]==2:
				if (OpeningType==2 or OpeningType==3): offsetapply[-4:] = Offsets[-4:]
				else: offsetapply[-4:] = OffsetsNone[-4:]
			if x == 0 and fmod(EdgeType,2): edgeapply=1
			if x == len(lowsegs)-2 and (EdgeType> 1): edgeapply += 2
			speclist.append((lowsegs[x][1], 0, lowsegs[x+1][0]-lowsegs[x][1], DoorHeight-WindowHeight, 0, 0, offsetapply, edgeapply, 0))
			offsetapply=[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
			
		edgeapply = 0

	
	#spaces between the doors and windows, middle height
	for x in range(len(midsegs)-1):
		if midsegs[x+1][0]-midsegs[x][1]>0:
			if (midsegs[x][3]==1 and (OpeningType==1 or OpeningType==3)) or (midsegs[x][3]==2 and (OpeningType==2 or OpeningType==3)): offsetapply[:4] = Offsets[:4]
			elif midsegs[x][2]==1: offsetapply[:4] = OffsetsNone[:4]
			if (midsegs[x+1][3]==1 and (OpeningType==1 or OpeningType==3)) or (midsegs[x+1][3]==2 and (OpeningType==2 or OpeningType==3)): offsetapply[-4:] = Offsets[-4:]
			elif midsegs[x+1][2]==1:offsetapply[-4:] = OffsetsNone[-4:]
			if x == 0 and fmod(EdgeType,2): edgeapply=1
			if x == len(midsegs)-2 and (EdgeType> 1): edgeapply += 2
			speclist.append((midsegs[x][1], DoorHeight-WindowHeight, midsegs[x+1][0]-midsegs[x][1], WindowHeight, 0, 0, offsetapply[:], edgeapply, 0))
			offsetapply=[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]			
			edgeapply = 0
	
	if not DoorHeight: ave = 0
	# Rows between lintels
	for x in range(len(midsegs)-1):
		if midsegs[x+1][0]-midsegs[x][1]-midsegs[x][2]*ave-midsegs[x+1][2]*ave>0:
			if x == 0 and fmod(EdgeType,2): edgeapply=1
			if x == len(midsegs)-2 and (EdgeType> 1): edgeapply += 2
			speclist.append((midsegs[x][1]+midsegs[x][2]*ave, DoorHeight, midsegs[x+1][0]-midsegs[x][1]-midsegs[x][2]*ave-midsegs[x+1][2]*ave, ave, 0, 0, offsetapply[:], edgeapply, 0))
			
			edgeapply = 0

	
	#Door and Window lintels
	for x in range(1,len(midsegs)-1):
		llen = ave
		rlen = ave
		if midsegs[x][0]-midsegs[x-1][1]<ave+ave*midsegs[x-1][2]:
			if midsegs[x-1][2]: llen = (midsegs[x][0]-midsegs[x-1][1])/2
			else: llen = midsegs[x][0]-midsegs[x-1][1]
			
		if midsegs[x+1][0]-midsegs[x][1]<ave+ave*midsegs[x+1][2]:
			if midsegs[x+1][2]: rlen = (midsegs[x+1][0]-midsegs[x][1])/2
			else: rlen = midsegs[x+1][0]-midsegs[x][1]

		speclist.append((midsegs[x][0]-llen, DoorHeight, midsegs[x][1]-midsegs[x][0]+llen+rlen, ave, 1, 1, LintelOffsets, edgeapply, 0))
	
	#Top Row
	tops = LinearFill(DoorHeight+ave,Height, ((Settings[0]+Settings[1])*(Settings[2]+Settings[3]))/Width*40, Settings[2]+Settings[3], Settings[2]-Settings[3])
	edgeapply = EdgeType
	for x in range(len(tops)-1):
		speclist.append((0, tops[x], Width, tops[x+1]-tops[x], 0,0,offsetapply, edgeapply, 0))
	
	edgeapply = 0
	
	#Crenelations
	for x, curcren in enumerate(crensegs):
		speclist.append((curcren[0], Height, curcren[1]-curcren[0], Crenel[1], 0, 0, Offsets, edgeapply, 0))

	
	
	return speclist


def main():
	__doc__ = """\
	The Main function isn't very useful right now.  If you wish to call the Auto_Masonry script
	with another script, call Spec_Generator and then WallGen as shown below (or just WallGen).
	Alternately, you can call RowsMaker to gain access directly to the points and faces.
	LinearFill can be useful for placing windows and doors if you write your own Spec.
	"""
	scn = Blender.Scene.GetCurrent()
	cur = scn.objects.active

	try:
		cur.select(0)
		if cur.type == 'Curve':
			applycurve = 1
			if cur.data.isCyclic():
				Width.val = PI*2*abs(cur.data.getSize()[1])
			
			else:
				tmpmesh = Blender.Mesh.New()
				tmpmesh.getFromObject(cur.name)
				Width.val = 0
				for ed in tmpmesh.edges: Width.val+=ed.length
		
		else: raise AttributeError
	
	except AttributeError:
		applycurve = 0

	scn.objects.selected = []
	
	if DoorActive.val or WindowActive.val:
		Doors = [DoorWidth.val,DoorHeight.val]	# element 0 is width, 1 is height, rest are center positions
		if DoorActive.val: Doors += [DoorSpacing.val]
		else: Doors += [None]
	
	else: Doors = None
	if WindowActive.val:
		Windows = [WindowWidth.val,WindowHeight.val]	# same as Doors
		if Doors[2]!=None: Windows += LinearFill(0,Doors[2]-Doors[0]/2, WindowSpacing.val, 2*Windows[0]/3,center=1)[1:-1]+LinearFill(Doors[2]+Doors[0]/2,Width.val, WindowSpacing.val, 2*Windows[0]/3,center=1)[1:-1]
		else: Windows += LinearFill(0,Width.val, WindowSpacing.val, 2*Windows[0]/3,center=1)[1:-1]

	else: Windows = None

	if CrenelActive.val: Crenel = [CrenelWidth.val, CrenelHeight.val] + LinearFill(0, Width.val, CrenelSpacing.val, CrenelWidth.val/2, center=1)[1:-1]
	else: Crenel = None
	
	Settings = MaxW.val, MinW.val, MaxH.val, MinH.val, MaxD.val, MinD.val, GroutWidth.val/2, GroutDepth.val, Bevel.val, HWeight.val
	
	tstart = time.clock()
	Spec = Spec_Generator(Settings, Width.val, Height.val, Taper.val, WindowAngle.val+DoorAngle.val*2, (EdgeType.val-1)*3, Doors, Windows, Crenel)
	wall,grout = WallGen(Settings, Spec)
	print "Wall generated in", str(time.clock()-tstart), "seconds."
	
	if applycurve:
		oldloc, cur.loc = cur.loc, [0.,0.,0.]
		oldrot, cur.rot = [cur.RotX, cur.RotY, cur.RotZ], [0.,0.,0.]
		oldsize, cur.size = cur.size, [1.,1.,1.]
		scn.objects.active = cur
		Blender.Window.EditMode(1)
		Blender.Draw.Redraw(1)
		Blender.Window.EditMode(0)
		mod = wall.modifiers.append(Blender.Modifier.Types.CURVE)
		mod[Blender.Modifier.Settings.OBJECT] = cur
		mod = grout.modifiers.append(Blender.Modifier.Types.CURVE)
		mod[Blender.Modifier.Settings.OBJECT] = cur
		cur.makeParent([wall,grout])
		cur.loc, cur.rot, cur.size = oldloc, oldrot, oldsize
		cur.select(1)
		Blender.Window.EditMode(1)
		Blender.Draw.Redraw(1)
		Blender.Window.EditMode(0)
		


def draw():
	global make
	global MaxW, MinW, MaxH, MinH, MaxD, MinD, Taper, Width, GroutWidth, GroutDepth
	global Height, Bevel, HWeight, OpeningType, EdgeType, DoorWidth, WindowWidth, CrenelWidth
	global DoorHeight, WindowHeight, CrenelHeight, DoorSpacing, WindowSpacing, CrenelSpacing
	global DoorActive, WindowActive, CrenelActive, DoorAngle, WindowAngle
	
	make = Blender.Draw.PushButton("Make this wall!", 1, 10,400,100,25,"No really... it makes a wall.")
	
	Blender.Draw.Label('WALL', 10, 380, 300, 20)
	Width = Blender.Draw.Number('Wall Width', 2, 10, 350, 120, 30, Width.val, 0.0, 1000.0, 'The width of the wall (will be overridden if a curve is selected)')
	Height = Blender.Draw.Number('Wall Height', 3, 130, 350, 120, 30, Height.val, 0.0, 1000.0, 'The height of the wall')
	EdgeType = Blender.Draw.Menu('Edge Style%t|Straight Edges|Offset Edges', 4, 250, 350, 150, 30, EdgeType.val, 'set to "offset" if you want the wall to loop seamlessly')
	
	Blender.Draw.Label('STONES', 10, 320, 300, 20)
	MinW = Blender.Draw.Number('Min Width', 5, 10, 290, 110, 30, MinW.val, Bevel.val*2+GroutWidth.val, MaxW.val, 'The min width of a single stone.')
	MaxW = Blender.Draw.Number('Max Width', 6, 120, 290, 110, 30, MaxW.val, MinW.val, 100.0, 'The max width of a single stone.')
	Blender.Draw.Label('X', 230, 290, 30, 30)	
	MinD = Blender.Draw.Number('Min Depth', 7, 10, 260, 110, 30, MinD.val, Bevel.val*2, MaxD.val, 'The min thickness of a single stone.')
	MaxD = Blender.Draw.Number('Max Depth', 8, 120, 260, 110, 30, MaxD.val, MinD.val, 100, 'The max thickness of a single stone.')
	Blender.Draw.Label('Y', 230, 260, 30, 30)
	MinH = Blender.Draw.Number('Min Height', 9, 10, 230, 110, 30, MinH.val, Bevel.val*2+GroutWidth.val, MaxH.val, 'The min height of a single stone.')
	MaxH = Blender.Draw.Number('Max Height', 10, 120, 230, 110, 30, MaxH.val, MinH.val, 100, 'The max height of a single stone.')
	Blender.Draw.Label('Z', 230, 230, 30, 30)
	Bevel = Blender.Draw.Number('Bevel', 11, 290, 290, 110, 30, Bevel.val, 0.0, MinH.val/2, 'The distance to bevel the edges of the stones.')
	GroutWidth = Blender.Draw.Number('Grout W', 12, 290, 260, 110, 30, GroutWidth.val, 0.0, MinH.val/2, 'The width of the grout(reduces the final stone width and height).')
	GroutDepth = Blender.Draw.Number('Grout D', 13, 290, 230, 110, 30, GroutDepth.val, -MaxD.val+MinD.val, MinD.val/2, 'The depth from the face of the shortest stones to the start of the grout.')
	HWeight = Blender.Draw.Number('Row Weight', 14, 10, 190, 110, 30, HWeight.val, -4.0, 4.0, 'How much the stone height affects the stone width')
#	Taper = Blender.Draw.Number('Row Weight', 15, 130, 190, 110, 30, Taper.val, -4.0, 4.0, 'value of 1 == the wall tapers to a point on top')
	
	DoorActive = Blender.Draw.Toggle('DOOR', 16, 10, 160, 300, 20, DoorActive.val, 'Do you want a door?')
	DoorAngle = Blender.Draw.Toggle('Angled', 17, 310, 160, 90, 20, DoorAngle.val, 'Do you want the door edges angled?')
	DoorHeight = Blender.Draw.Number('Height', 18, 10, 140, 110, 20, DoorHeight.val, 0.0, Height.val, 'also sets the window tops')
	DoorWidth = Blender.Draw.Number('Width', 19, 120, 140, 110, 20, DoorWidth.val, 0.0, Width.val, 'The door width')
	DoorSpacing = Blender.Draw.Number('Position', 20, 230, 140, 110, 20, DoorSpacing.val, 0.0, Width.val, 'The x position of the door')

	WindowActive = Blender.Draw.Toggle('WINDOWS', 21, 10, 110, 300, 20, WindowActive.val, 'Do you want windows?')
	WindowAngle = Blender.Draw.Toggle('Angled', 22, 310, 110, 90, 20, WindowAngle.val, 'Do you want the window edges angled?')
	WindowHeight = Blender.Draw.Number('Height', 23, 10, 90, 110, 20, WindowHeight.val, 0.0, DoorHeight.val, 'The window height')
	WindowWidth = Blender.Draw.Number('Width', 24, 120, 90, 110, 20, WindowWidth.val, 0.0, Width.val, 'The window width')
	WindowSpacing = Blender.Draw.Number('Spacing', 25, 230, 90, 110, 20, WindowSpacing.val, 0.0, Width.val, 'The window spacing, center to center')

	CrenelActive = Blender.Draw.Toggle('CRENELS', 26, 10, 60, 300, 20, CrenelActive.val, 'Do you want crennelations?')
	CrenelHeight = Blender.Draw.Number('Height', 27, 10, 40, 110, 20, CrenelHeight.val, 0.0, Height.val, 'The crenel height')
	CrenelWidth = Blender.Draw.Number('Width', 28, 120, 40, 110, 20, CrenelWidth.val, 0.0, Width.val, 'The crenel width')
	CrenelSpacing = Blender.Draw.Number('Spacing', 29, 230, 40, 110, 20, CrenelSpacing.val, 0.0, Width.val, 'The crenel spacing, center to center')

	exunt = Blender.Draw.PushButton("Exit", 0, 150,10,100,20)

def event(evt,val):
	if (evt==Blender.Draw.QKEY and not val): Blender.Draw.Exit()
	if (evt==Blender.Draw.RETKEY and not val): main()

def bevent(evt):
	if evt == 0: Blender.Draw.Exit()
	if evt == 1: main()
	if 1<evt<30: Blender.Draw.Redraw(0)


if __name__ == '__main__':
	Blender.Draw.Register(draw, event, bevent)