#!BPY

# """
# Name: 'Auto Tile'
# Blender: 248
# Group: 'Object'
# Tooltip: 'creates tiled clones'
# """

__author__ = 'Paul Spooner, aka "Dudecon" or "Ziggy"'
__version__ = '0.3 2007/09/22'
__url__ = ["Author's site, http://www.peripheralarbor.com", "http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Object/Auto_Tile"]
__email__ = ["Author's personal e-mail, dudecon:hotmail*com"]
__bpydoc__ = """\
This script creates tiled spaces with clone copies of the selected object.

# Version History:

# V 0.1 2007/9/18		First release

# V 0.2 2007/9/20		Added the boundary filling option

# V 0.3 2007/9/22		Added lattice deform and a short description to presets
"""

# -----------------------------------
# Auto_Tile.py (c) Paul Spooner 2009
# -------------------------------------------------------------------------- 
# ***** BEGIN GPL LICENSE BLOCK ***** 
# 
# 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 ***** 
# -------------------------------------------------------------------------- 

from Blender import *
from Blender.Mathutils import Vector
import math
from math import fmod, floor

PI = math.pi

#GLOBAL MENU VARIABLES
scn = Scene.GetCurrent()
ob = scn.objects.active
xnum = Draw.Create(8) # number of columns
ynum = Draw.Create(8) # number of rows
xsize = Draw.Create(1.0) # spacing between columns
ysize = Draw.Create(1.0) # spacing between rows
xrot = Draw.Create(0)
yrot = Draw.Create(0)
xmir = Draw.Create(0)
xrim = Draw.Create(0)
ymir = Draw.Create(0)
yrim = Draw.Create(0)
xrot2 = Draw.Create(0)
yrot2 = Draw.Create(0)
xmir2 = Draw.Create(0)
xrim2 = Draw.Create(0)
ymir2 = Draw.Create(0)
yrim2 = Draw.Create(0)
dub = Draw.Create(0)
mir = Draw.Create(0)
exrot = Draw.Create(0)
el = Draw.Create(3)
Presets = Draw.Create(1)
PresetText = "Presets%t|Square 1 (basic square and triangle tile)|Square 2 (x mirrored rows)|Square 3 (x mirrored rows and columns)|Square 4 (x mirrored columns)|Square 5 (rotated pairs)|Square 6 (rotated quads)|Square 7 (mirrored pairs)|Square 8 (mirrored quads, ie seamless)|Square 9 (offset mirrored quads)|Square 10 (mirrored rotated quads)|Square 11 (sets of eight, rotated and mirrored)|%l|Hexagonal|Triangular 1 (sets of three)|Triangular 2 (mirrored sets)|Triangular 3 (mirrored sets, offset)|Triangular 4 (mirrored sets of 12)"
boundname = Draw.Create("")
addlattice = Draw.Create(0)
bounds = None
Go = 0


#Quick Documentation:
#set 'number' below to set many times to tile
#at the end of the script call one of the functions (1 thru 17) and it will
#make clone copies of the selected object at the origin

#if you are using the "TileExample.blend" file, for best results:
#select Cube for patterns 1 to 11
#select Cube.001 for pattern 12
#select Cylinder for patterns 13-16
#select Cylinder.001 for pattern 17

def squarepattern(xnum,ynum,xsize,ysize,xrot = 0, yrot = 0, xmir = 0, xrim = 0, ymir = 0, yrim = 0, xrot2 = 0, yrot2 = 0, xmir2 = 0, xrim2 = 0, ymir2 = 0, yrim2 = 0, dub = 0,xstart = 0, ystart = 0):
	global Go, bounds
	for x in range(xstart,xnum):
		for y in range(ystart,ynum):
			if bounds:
				for face in bounds.faces:
					if Geometry.PointInTriangle2D(Vector([x*xsize,y*ysize,0]), Vector(face.verts[0].co), Vector(face.verts[2].co), Vector(face.verts[1].co)) or Geometry.PointInTriangle2D(Vector([x*xsize,y*ysize,0]), Vector(face.verts[0].co), Vector(face.verts[1].co), Vector(face.verts[2].co)):
						Go = 1
						break
					elif len(face.verts) == 4 and (Geometry.PointInTriangle2D(Vector([x*xsize,y*ysize,0]), Vector(face.verts[0].co), Vector(face.verts[2].co), Vector(face.verts[3].co)) or Geometry.PointInTriangle2D(Vector([x*xsize,y*ysize,0]), Mathutils.Vector(face.verts[0].co), Vector(face.verts[3].co), Vector(face.verts[2].co))):
						Go = 1
						break
			
			else: Go = 1
			
			if not Go: continue
			
			newob = scn.objects.new(ob.data, ob.name)
			newob.loc = [x*xsize,y*ysize,0]
			if fmod(x,2):
				newob.RotZ += xrot*PI/2
				if xmir: newob.SizeX = -1*newob.SizeX
				if xrim: newob.SizeY = -1*newob.SizeY
			if fmod(x/2,2):
				newob.RotZ += xrot2*PI/2
				if xmir2: newob.SizeX = -1*newob.SizeX
				if xrim2: newob.SizeY = -1*newob.SizeY
			if fmod(y,2):
				newob.RotZ += -yrot*PI/2
				if ymir: newob.SizeY = -1*newob.SizeY
				if yrim: newob.SizeX = -1*newob.SizeX
			if fmod(y/2,2):
				newob.RotZ += -yrot2*PI/2
				if ymir2: newob.SizeY = -1*newob.SizeY
				if yrim2: newob.SizeX = -1*newob.SizeX
			if (fmod(y,2) and fmod(x,2)):
				newob.RotZ += xrot*yrot*PI
			if (fmod(y/2,2) and fmod(x/2,2)):
				newob.RotZ += xrot2*yrot2*PI
			if dub:
				newob.RotZ += -newob.SizeX*PI/2
				newob.SizeX = -1*newob.SizeX
			
			Go = 0



def hexpattern(xnum,ynum,xsize,ysize,mir=0,exrot=0,el=2,dub=0,xstart = 0, ystart = 0):
	global Go, bounds
	for x in range(xstart,xnum):
		for y in range(ystart,ynum):
			if x: loc = [xsize*x*3/2.0,ysize*(y*2-fmod(x,2)*(x/abs(x)))*math.sqrt(3)/2.0,0]
			else: loc = [0,ysize*(y*2-fmod(x,2))*math.sqrt(3)/2.0,0]

			if bounds:
				for face in bounds.faces:
					if Geometry.PointInTriangle2D(Vector(loc), Vector(face.verts[0].co), Vector(face.verts[2].co), Vector(face.verts[1].co)) or Geometry.PointInTriangle2D(Vector(loc), Vector(face.verts[0].co), Vector(face.verts[1].co), Vector(face.verts[2].co)):
						Go = 1
						break
					elif len(face.verts) == 4 and (Geometry.PointInTriangle2D(Vector(loc), Vector(face.verts[0].co), Vector(face.verts[2].co), Vector(face.verts[3].co)) or Geometry.PointInTriangle2D(Vector(loc), Mathutils.Vector(face.verts[0].co), Vector(face.verts[3].co), Vector(face.verts[2].co))):
						Go = 1
						break
			
			else: Go = 1
			
			if not Go: continue
			if el==0:
				newob = scn.objects.new(ob.data, ob.name)
				newob.loc = loc
				newob.RotZ += exrot*PI/6.0
				if fmod(x,2): newob.RotZ += PI
				Go = 0
				continue
			for i in range(6):
				if el==1 and fmod(i,2): continue
				newob = scn.objects.new(ob.data, ob.name)
				newob.loc = loc
				newob.RotZ = PI*(i/3.)-exrot*PI/6.0
				if dub: newob.SizeX = -1
				if mir and fmod(i,2): newob.SizeX = -1*newob.SizeX
				Go = 0


def main(type=0):
	global scn, ob, corners, bounds
	scn = Scene.GetCurrent()
	ob = scn.objects.active
	if ob == None: return
	
	try: bob = Object.Get(boundname.val)
	except: bob = None
	if bob and bob.type == 'Mesh': bounds = bob.getData(mesh=True)
	elif bob and bob.type == 'Curve' and bob.data.isCyclic():
		bounds = Mesh.New()
		bounds.getFromObject(bob.name)
	
	if bob:
		oldloc, bob.loc = bob.loc, [0.,0.,0.]
		oldrot, bob.rot = [bob.RotX, bob.RotY, bob.RotZ], [0.,0.,0.]
		oldsize, bob.size = bob.size, [1.,1.,1.]
		scn.objects.active = bob
		Window.RedrawAll()
		corners = [bob.boundingBox[0][0]/xsize.val,bob.boundingBox[0][1]/ysize.val , bob.boundingBox[7][0]/xsize.val, bob.boundingBox[7][1]/ysize.val]
		if type == 1: corners = [corners[0]*2/3,corners[1]/math.sqrt(3),corners[2]*2/3,corners[3]/math.sqrt(3)]
		
	else: corners = [0,0,xnum.val-1,ynum.val-1]
	
	scn.objects.selected = []
	if type == 0:
		squarepattern(int(corners[2])+1,int(corners[3])+1,xsize.val,ysize.val,xrot.val, yrot.val, xmir.val, xrim.val, ymir.val, yrim.val, xrot2.val, yrot2.val, xmir2.val, xrim2.val, ymir2.val, yrim2.val,0,int(corners[0]),int(corners[1]))
		if dub.val: squarepattern(int(corners[2])+1,int(corners[3])+1,xsize.val,ysize.val,xrot.val, yrot.val, xmir.val, xrim.val, ymir.val, yrim.val, xrot2.val, yrot2.val, xmir2.val, xrim2.val, ymir2.val, yrim2.val, dub.val,int(corners[0]),int(corners[1]))
	
	elif type == 1:
		hexpattern(int(corners[2])+1,int(corners[3])+1,xsize.val,ysize.val,mir.val,exrot.val,el.val-1,0,int(corners[0]),int(corners[1]))
		if dub.val: hexpattern(int(corners[2])+1,int(corners[3])+1,xsize.val,ysize.val,mir.val,exrot.val,el.val-1,dub.val,int(corners[0]),int(corners[1]))
		
	if addlattice.val:
		latdata = Lattice.New()
		#latdata.setPartitions(2,2,2)
		latob = scn.objects.new(latdata)
		latob.size = [(corners[2]-corners[0])*xsize.val,(corners[3]-corners[1])*ysize.val,1]
		for ob in scn.objects.selected:
			if ob == latob: continue
			mod= ob.modifiers.append(Modifier.Type.LATTICE)
			mod[Modifier.Settings.OBJECT] = latob
			ob.makeDisplayList()
			
	if bob:
		bob.makeParent(scn.objects.selected)
		bob.loc, bob.rot, bob.size = oldloc, oldrot, oldsize
		scn.objects.active = ob
	
	elif addlattice.val: latob.loc = [(xnum.val-1)*xsize.val/2., (ynum.val-1)*ysize.val/2.,0]
	
	Window.RedrawAll()

def buttonreset():
	xrot.val = 0
	yrot.val = 0
	xmir.val = 0
	xrim.val = 0
	ymir.val = 0
	yrim.val = 0
	xrot2.val = 0
	yrot2.val = 0
	xmir2.val = 0
	xrim2.val = 0
	ymir2.val = 0
	yrim2.val = 0
	dub.val = 0
	mir.val = 0
	exrot.val = 0
	el.val = 3


def draw():
	global sqr, hex, exit, Presets, PresetText, boundname, addlattice
	global xnum, ynum, xsize, ysize, xrot, yrot, xmir, xrim, ymir, yrim
	global xrot2, yrot2, xmir2, xrim2, ymir2, yrim2, dub, mir, exrot, el
	
	Draw.Label("Duplicate and Tile",10,390,300,30)
	boundname = Draw.String("Boundary Object: ", 23, 10, 350,300,30,boundname.val,50,"This mesh or closed curve will be filled in its local x-y plane, overrides 'NumX' and 'NumY'")
	xnum = Draw.Number("Num X",4,10, 320, 80, 30,xnum.val,1,500,"Number of columns")
	ynum = Draw.Number("Num Y",5,90, 320, 80, 30,ynum.val,1,500,"Number of rows")
	xsize = Draw.Number("Dim X",6,170, 320, 80, 30,xsize.val,0.0,50.0,"Spacing of columns (times 2*sqrt(3) for hex)")
	ysize = Draw.Number("Dim Y",7,250, 320, 80, 30,ysize.val,0.0,50.0,"Spacing of rows (times 2 for hex)")
	
	Presets = Draw.Menu(PresetText,1,10,280,300,30,Presets.val,"Some nice tiling settings")
	
	sqr = Draw.PushButton("Square pattern", 2,10,240,200,30,"Makes a square pattern with the active object")
	Draw.Label("Every column/row",10,220,200,20)
	Draw.Label("On X",10,200,40,20)
	xmir = Draw.Toggle("MirX",8,50,200,30,20,xmir.val,"Mirror on the X axis as X increases")
	xrim = Draw.Toggle("MirY",9,80,200,30,20,xrim.val,"Mirror on the Y axis as X increases")
	xrot = Draw.Number("Rot",10,110,200,80,20,xrot.val,0,2,"How much to rotate each time X increases")
	Draw.Label("On Y",10,180,40,20)
	yrim = Draw.Toggle("MirX",11,50,180,30,20,yrim.val,"Mirror on the X axis as Y increases")
	ymir = Draw.Toggle("MirY",12,80,180,30,20,ymir.val,"Mirror on the Y axis as Y increases")
	yrot = Draw.Number("Rot",13,110,180,80,20,yrot.val,0,2,"How much to rotate each time Y increases")
	
	Draw.Label("Every two columns/rows",10,160,200,20)
	Draw.Label("On X",10,140,40,20)
	xmir2 = Draw.Toggle("MirX",14,50,140,30,20,xmir2.val,"Mirror on the X axis as X increases by two")
	xrim2 = Draw.Toggle("MirY",15,80,140,30,20,xrim2.val,"Mirror on the Y axis as X increases by two")
	xrot2 = Draw.Number("Rot",16,110,140,80,20,xrot2.val,-1,2,"How much to rotate each time X increases by two")
	Draw.Label("On Y",10,120,40,20)
	yrim2 = Draw.Toggle("MirX",17,50,120,30,20,yrim2.val,"Mirror on the X axis as Y increases by two")
	ymir2 = Draw.Toggle("MirY",18,80,120,30,20,ymir2.val,"Mirror on the Y axis as Y increases by two")
	yrot2 = Draw.Number("Rot",19,110,120,80,20,yrot2.val,-1,2,"How much to rotate each time Y increases by two")
	
	hex = Draw.PushButton("Hexagonal/triangular pattern", 3,10,70,200,30,"Makes a hexagonal pattern with the active object")
	mir = Draw.Toggle("Mir",20,10,40,40,20,mir.val,"Mirror every other element")
	exrot = Draw.Toggle("ExRot",21,60,40,40,20,exrot.val,"Adds an extra 30deg rotation to every element")
	el = Draw.Menu("Number Of Elements%t|One|Three|Six",22,100,40,100,20,el.val,"Chose how many elements per hexagon")
	
	Draw.Label("Misc Settings", 220,255,100,20)
	dub = Draw.Toggle("Double Elements",4,220,215,100,20,dub.val,"Enables mirrored and rotated copies at all locations, use with care")
	addlattice = Draw.Toggle("Lattice Deform", 24, 220, 235, 100, 20, addlattice.val, "creates a lattice and deforms all of the new objects with it")
	
	exit = Draw.PushButton("Exit", 0,150,10,100,20,"exits the script")

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

def bevent(evt):
	if evt == 0: Draw.Exit()
	if evt == 1:
		if Presets.val == 0: Return
		
		elif Presets.val == 1:
			buttonreset()
		
		elif Presets.val == 2:
			buttonreset()
			yrim.val=1
		
		elif Presets.val == 3:
			buttonreset()
			yrim.val=1
			xmir.val=1
		
		elif Presets.val == 4:
			buttonreset()
			xmir.val=1
		
		elif Presets.val == 5:
			buttonreset()
			xrot.val=2
			yrim.val=1
		
		elif Presets.val == 6:
			buttonreset()
			yrot.val=1
			xrot.val=1
		
		elif Presets.val == 7:
			buttonreset()
			yrot.val=2
			xmir.val=1
		
		elif Presets.val == 8:
			buttonreset()
			ymir.val=1
			xmir.val=1
		
		elif Presets.val == 9:
			buttonreset()
			ymir.val=1
			xmir.val=1
			xrim2.val=1
		
		elif Presets.val == 10:
			buttonreset()
			xrot.val=1
			yrot.val=1
			xmir2.val=1
			ymir2.val=1
			xrot2.val=-1
			yrot2.val=-1
		
		elif Presets.val == 11:
			buttonreset()
			yrot.val=1
			xrot.val=1
			dub.val=1
		
		elif Presets.val == 13:
			buttonreset()
			el.val=1
		
		elif Presets.val == 14:
			buttonreset()
			el.val=2
			exrot.val=1
		
		elif Presets.val == 15:
			buttonreset()
			mir.val=1
		
		elif Presets.val == 16:
			buttonreset()
			mir.val=1
			exrot.val=1
		
		elif Presets.val == 17:
			buttonreset()
			dub.val = 1
		
		Draw.Redraw(0)
			
	if evt == 2:
		main(0)

	if evt == 3:
		main(1)

	if 3<evt<23: Draw.Redraw(0)

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