#!BPY

""" Registration info for Blender menus: <- these words are ignored
Name: 'Image Geometry'
Blender: 248
Group: 'Image'
Tip: 'Creates mesh objects from image files'
"""

__author__ = "Kurt L. Whicher"
__url__ = ["http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Image/Image_Geometry"]

__bpydoc__ = """\
Script uses data from image files to create and modify mesh objects. Pixels are converted to "square",
"diamond" or "octagon" patterns within a mesh. The sizes of the primatives are determined by user settings
including combinations of RGBA channels. The intensity is used for sizing primatives. You can create mesh
geometry from either the pixels themselves (dots) or the surrounding area (the web) or keep both of these
regions and extrude.

THE GRAPHICAL USER INTERFACE:
When you start the script, click on the Select a File button. After the file has been loaded, more buttons will
appear. The Set Width & Height menu to the upper left has three options:
* Set Width
* Set Height
* Set Both
If you choose either of the first two options, vertices in both directions have equal spacing in the xy plane.
If you set both, you will have greater control over the size of the mesh, but primatives that represent image
pixels may be stretched vertically or horizontally. The number buttons to the right of the Set Width & Height
buttons are where you set the values. You will only see both of these if you are setting all. If you're making
a "dots" mesh, the size of the mesh will be slightly smaller than the size that you set. This is because
primative sizes within the mesh will vary depending on pixel intensity. There are x/y contrast & brightness
settings. If you set Contrast to 1 (the maximum), primatives will have the greatest possible size variation
depending on the contrast in the original image. If you set it to 0 (the minimum) there will be no variation in
the primatives. Brightness has more relevance when Contrast is set to lower values. As a matter of fact, it
will have no influence when Contrast is set to 1.0. If you set contrast to a lower value, you can get smaller
or larger primatives by adjusting Brighness.
The Mesh Type menu has the following options:
* Web
* Dots
* Extrusion
Web produces a mesh with cut-outs for whatever type of primative is selected from the Primative Pattern menu.
Think of a screen. The dots option will produce the primatives but not the web surrounding them. Extrusions
produce the web, the dots and extruded faces between. For dots & extrusions, there is an option to use a
material range. If this is toggled on, a numButton will allow the user to set the number of materials.
Interpolation is used for materials in a range. Material settings are very minimal just allowing for adjusting
colors and emit levels.
There are a lot of settings for extrusions including taper adjustments and a toggle to set extruded faces to
the same material as the web or dots (material range only). Extrusions can be up or down along the z axis,
extrusion distances can be set, and there is a toggle to use intensity data for extrusion distances.
Color pickers and emit numButtons are displayed depending on other settings.
The primative options include squares, octagons and diamonds.
There is a toggle to reverse image intensity. Think of a negative of the image.
The RGBA Channels menu allows you to select what channels to use when gathering intensity data. These are the
options:
 * RGB
 * RGBA
 * R
 * G
 * B
 * A
You probably don't want to use A or RGBA on images without any transparent areas since A will just produce a
uniform pattern and RGBA will dilute the intensity.
There are buttons to create the mesh or create the mesh along with a sample "script link" script for producing
an animation using a set of images from a real animation. When setting this up, be sure to read everything
carefully before proceeding. Everything needs to be setup properly as the script doesn't yet do any error
checking.

WARNINGS:
* Try not to use very large images since this may create several vertices and faces for each pixel.
 With an image file of size (x,y), here are the numbers:
  - dots mesh: 9xy vertices, 4xy faces
  - web mesh: 8xy + 6x + 6y + 4 vertices, 5xy + 3x + 3y + 1 faces
  - extrusion mesh: 17xy + 6x + 6y + 4 vertices, 17xy + 3x + 3y + 1 faces
 So, if you started with an image with 80 X 60 or 4800 pixels, you would have:
  - dots mesh: 43,200 vertices and 19,200 faces
  - web mesh: 39,244 vertices and 24,421 faces
  - extrusion mesh: 82,444 vertices and 82,021 faces
* Couldnot get the script to work with some jpg images. If you have problems, you might want to try using a
different file format.

If you just want to run the script, you can safely ignore the rest of the documentation.
_______________________________________________________________________________________________________________

GLOBAL VARIABLES:
WEB=1 -- mesh type constant
DOTS=2 -- mesh type constant
EXTRUDE=3 -- mesh type constant
MY_IMAGE=None -- the image object
ANIMATION_IMAGE -- for possible later use with script links
MU_WIDTH_HEIGHT=100 -- gui constant
NM_WIDTH=110 -- gui constant
NM_HEIGHT=120 -- gui constant
NM_X_CONTRAST=130 -- gui constant
NM_X_BRIGHTNESS=140 -- gui constant
NM_Y_CONTRAST=150 -- gui constant
NM_Y_BRIGHTNESS=160 -- gui constant
MU_MESH_TYPE=170 -- gui constant
TG_MATERIAL_RANGE=180 -- gui constant
NM_MATS=190 -- gui constant
NM_EXT_TAPER=200 -- gui constant
TG_DOTS_EXT_SAME_MAT=210 -- gui constant
CP_1=250 -- gui constant
CP_2=260 -- gui constant
CP_3=270 -- gui constant
NM_EMIT_1=280 -- gui constant
NM_EMIT_2=290 -- gui constant
NM_EMIT_3=300 -- gui constant
NM_EXT_DIRECTION=310 -- gui constant
NM_EXT_DISTANCE=320 -- gui constant
TG_INTENSITY_EXTRUDE=330 -- gui constant
MU_PRIMATIVE=340 -- gui constant
TG_REVERSE=350 -- gui constant
MU_RGBA=360 -- gui constant
PB_CREATE_MESH=370 -- gui constant
PB_MAKE_MESH_N_SCRIPT=380 -- gui constant
PB_SELECT_FILE=390 -- gui constant
PB_EXIT_SCRIPT=400 -- gui constant
D=Draw.Create -- gui constant
GUI={} -- gui dictionary
GUI['WOH']=D(1) -- gui dictionary item (width or height)
GUI['W']=D(6.0) -- gui dictionary item (width)
GUI['H']=D(6.0) -- gui dictionary item (height)
GUI['XCO']=D(1.0) -- gui dictionary item (x contrast)
GUI['XBR']=D(1.0) -- gui dictionary item (x brightness)
GUI['YCO']=D(1.0) -- gui dictionary item (y contrast)
GUI['YBR']=D(1.0) -- gui dictionary item (y brightness)
GUI['MT']=D(EXTRUDE) -- gui dictionary item (mesh type)
GUI['MRG']=D(1) -- gui dictionary item (material range for dots or extrusions; 1:True, 0:False)
GUI['NM']=D(5) -- gui dictionary item (number of materials)
GUI['ET']=D(.9) -- gui dictionary item (extrusion taper)
GUI['DE']=D(1) -- gui dictionary item (dots/extrude same material)
GUI['C0']=D(1.0,.0,.0) -- gui dictionary item (color 0)
GUI['C1']=D(.0,1.0,.0) -- gui dictionary item (color 1)
GUI['C2']=D(.0,.0,1.0) -- gui dictionary item (color 2)
GUI['E0']=D(.0) -- gui dictionary item (emit 0)
GUI['E1']=D(.5) -- gui dictionary item (emit 1)
GUI['E2']=D(1.0) -- gui dictionary item (emit 2)
GUI['EXDR']=D(0) -- gui dictionary item (extrusion direction)
GUI['EXDS']=D(.3) -- gui dictionary item (extrusion distance)
GUI['IE']=D(1) -- gui dictionary item (intensity extrude)
GUI['PP']=D(1) -- gui dictionary item (primative pattern)
GUI['RV']=D(0) -- gui dictionary item (reverse intensity)
GUI['RGBA']=D(0) -- gui dictionary item (rgba channels)
GUI_STRING="" -- holds gui string
GUI_BLOCK=[] -- holds gui data items
GUI_BLOCK_ROW=[] -- holds gui data items (rows)
CCHS=[True,True,True,False] -- color channels
OPF=1.5 -- one point five ()
AMN=.1 -- absolute minimum
DA=OPF-2*AMN -- distance available
WRITE_SCRIPT -- should write scriptlink script (bool)
"""
# --------------------------------------------------------------------------
# image_geometry.py (c) Kurt L. Whicher
# --------------------------------------------------------------------------
# ***** 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 *****
# --------------------------------------------------------------------------

import copy,math
from Blender import BGL,Draw,Image,Material,Mesh,Redraw,Scene,Window

WEB=1
DOTS=2
EXTRUDE=3
MY_IMAGE=None
ANIMATION_IMAGE=None
MU_WIDTH_HEIGHT=100
NM_WIDTH=110
NM_HEIGHT=120
NM_X_CONTRAST=130
NM_X_BRIGHTNESS=140
NM_Y_CONTRAST=150
NM_Y_BRIGHTNESS=160
MU_MESH_TYPE=170
TG_MATERIAL_RANGE=180
NM_MATS=190
NM_EXT_TAPER=200
TG_DOTS_EXT_SAME_MAT=210
CP_1=250
CP_2=260
CP_3=270
NM_EMIT_1=280
NM_EMIT_2=290
NM_EMIT_3=300
NM_EXT_DIRECTION=310
NM_EXT_DISTANCE=320
TG_INTENSITY_EXTRUDE=330
MU_PRIMATIVE=340
TG_REVERSE=350
MU_RGBA=360
PB_CREATE_MESH=370
PB_MAKE_MESH_N_SCRIPT=380
PB_SELECT_FILE=390
PB_EXIT_SCRIPT=400
D=Draw.Create
GUI={}
GUI['WOH']=D(1)
GUI['W']=D(6.0)
GUI['H']=D(6.0)
GUI['XCO']=D(1.0)
GUI['XBR']=D(1.0)
GUI['YCO']=D(1.0)
GUI['YBR']=D(1.0)
GUI['MT']=D(EXTRUDE)
GUI['MRG']=D(1)
GUI['NM']=D(6)
GUI['ET']=D(.9)
GUI['DE']=D(1)
GUI['C0']=D(1.0,.0,.0)
GUI['C1']=D(.0,1.0,.0)
GUI['C2']=D(.0,.0,1.0)
GUI['E0']=D(.0)
GUI['E1']=D(.1)
GUI['E2']=D(.2)
GUI['EXDR']=D(0)
GUI['EXDS']=D(.5)
GUI['IE']=D(1)
GUI['PP']=D(1)
GUI['RV']=D(0)
GUI['RGBA']=D(0)
GUI_STRING=""
GUI_BLOCK=[]
GUI_BLOCK_ROW=[]
CCHS=[True,True,True,False]
OPF=1.5
AMN=.1
DA=OPF-2*AMN
WRITE_SCRIPT=False
SAMPLE_ANIMATION_SCRIPT='''\
# PLACEHOLDER FOR SCRIPT LINK TEXT BLOCK
'''
def main():
	__doc__ = '''\
	'''
	Redraw(-1)

def gui():
	__doc__ = '''\
	Function is longer than others but simple. The m var is an alias for gui_magic, and almost everything
	here is just a call to m. Globals GUI_STRING, GUI_BLOCK & GUI_BLOCK_ROW are all cleared.
	Here are the different ways to call gui_magic:
	 * m(t,"a string") -- Send it a text string to store and draw later.
	 * m(r) -- Start a new row. (This must follow every 1 to 3 items to be drawn)
	 * m(mu,"menu formatting string",bevent int,"GUI dict name","tooltip") -- Store draw menu.
	 * m(nm,"numButton text",bevent int,"GUI dict name",min,max,"tooltip") -- Store num button.
	 * m(cp,bevent int,"GUI dict name") -- Store color picker
	 * m(pb,"button text",bevent int,"tooltip") -- Store push button.
	 * m(tg,"button text",bevent int,"GUI dict name","tooltip") -- Store toggle button.
	 * m("write") -- Parse and write all the data to global GUI_STRING.
	The last step is just to call exec(GUI_STRING) to draw the interface. Note that r, cp, mu, nm, pb, t
	& tg are all strings. The idea is to set things up once to code up guis quickly.
	'''
	global GUI,GUI_STRING,GUI_BLOCK,GUI_BLOCK_ROW
	GUI_STRING=""
	GUI_BLOCK=[]
	GUI_BLOCK_ROW=[]
	m=gui_magic
	cp,mu,nm,pb,r,t,tg="colpik","menu","num","button","row","text","tog"
	if MY_IMAGE:
		m(t," Grid spacing is proportional when setting width or height only.")
		m(r)
		whm="Set Width & Height %t|Set Width %x1|Set Height %x2|Set Both %x3"
		m(mu,whm,MU_WIDTH_HEIGHT,"WOH","set width, height or both")
		if GUI['WOH'].val!=2:
			m(nm,"Width",NM_WIDTH,"W",1.0,24.0,"set width value")
		if GUI['WOH'].val!=1:
			m(nm,"Height",NM_HEIGHT,"H",1.0,24.0,"set height value")
		m(r)
		m(nm,'X "Contrast":',NM_X_CONTRAST,"XCO",.0,1.0,"set x range")
		if GUI['XCO'].val!=1.0:
			m(nm,'X "Brightness":',NM_X_BRIGHTNESS,"XBR",.0,1.0,"set x size of primatives")
		m(r)
		m(nm,'Y "Contrast":',NM_Y_CONTRAST,"YCO",.0,1.0,"set y range")
		if GUI['YCO'].val!=1.0:
			m(nm,'Y "Brightness":',NM_Y_BRIGHTNESS,"YBR",.0,1.0,"set y size of primatives")
		m(r)
		mtm="Mesh Type %t|Web %x1|Dots %x2|Extrusion %x3"
		m(mu,mtm,MU_MESH_TYPE,"MT","select a mesh type")
		if GUI['MT'].val==EXTRUDE or GUI['MT'].val==DOTS:
			m(tg,"Use Material Range",TG_MATERIAL_RANGE,"MRG","select to use color range")
			if GUI['MRG'].val:
				m(nm,"Number of Mats:",NM_MATS,"NM",3,16,"select number of materials")
		m(r)
		if GUI['MT'].val==DOTS:
			if GUI['MRG'].val:
				m(t," Materials 1 & 2 are the range limits. Others interpolated between.")
				m(r)
		if GUI['MT'].val==EXTRUDE:
			m(nm,"Set Extrusion Taper",NM_EXT_TAPER,"ET",.2,1.0,"set extrusion taper")
			if GUI['MRG'].val:
				m(tg,"Same Mat for Dots & Extrusion",TG_DOTS_EXT_SAME_MAT,"DE",\
					"use dots material for extrusion")
				m(r)
				if GUI['DE'].val:
					m(t," Mat 1 is the web, 2 & 3 are limits for dots/stretched regions.")
				else:
					m(t," Mat 1 is the web/stretched region, 2 & 3 are limits for dots.")
				m(r)
			else:
				m(r)
				m(t," mat 1 is the web, mat 2 is extruded, mat 3 is the dots:")
				m(r)
		m(t," Material 1 Settings:")
		m(cp,CP_1,"C0")
		m(nm,"Mat 1 Emit",NM_EMIT_1,"E0",.0,1.0,"set light emit level")
		m(r)
		if (GUI['MT'].val==DOTS and GUI['MRG'].val)or GUI['MT'].val==EXTRUDE:
			m(t," Material 2 Settings:")
			m(cp,CP_2,"C1")
			m(nm,"Mat 2 Emit",NM_EMIT_2,"E1",.0,1.0,"set light emit level")
			m(r)
		if GUI['MT'].val==EXTRUDE:
			m(t," Material 3 Settings:")
			m(cp,CP_3,"C2")
			m(nm,"Mat 3 Emit",NM_EMIT_3,"E2",.0,1.0,"set light emit level")
			m(r)
			edm="Extrusion Direction %t|Extrude Up %x0|Extrude Down %x1"
			m(mu,edm,NM_EXT_DIRECTION,"EXDR","select an extrusion direction")
			m(nm,"Ext Dist",NM_EXT_DISTANCE,"EXDS",.0,2.0,"set extrusion distance")
			m(tg,"Intensity Extrusion",TG_INTENSITY_EXTRUDE,"IE","use intensity")
			m(r)
		ppm="Primative Pattern %t|Square %x1|Diamond %x2|Octagon %x3"
		m(mu,ppm,MU_PRIMATIVE,"PP","select a primative type")
		m(tg,"Reverse",TG_REVERSE,"RV","reverse pixel intensity")
		rcm="RGBA Channels %t|RGB %x0|RGBA %x1|R %x2|G %x3|B %x4|A %x5"
		m(mu,rcm,MU_RGBA,"RGBA","select RGBA channels to use for geometry")
		m(r)
		#m(t," If you just want to create the object, click the following:")
		#m(r)
		m(pb,"Make Image Mesh",PB_CREATE_MESH,"push to create the mesh")
		m(r)
		# THE FOLLOWING IS INTENDED FOR LATER IMPLEMENTATION OF SCRIPT LINKS
		#m(t," If you want to create an animation with a series of images, click on the")
		#m(r)
		#m(t," following button. Be sure to read over script documentation.")
		#m(r)
		#m(pb,"Make Mesh & Write Script",PB_MAKE_MESH_N_SCRIPT,"make mesh & animation script")
		#m(r)
	else:
		m(t," Select a file to start.")
		m(r)
	m(pb,"Select a File",PB_SELECT_FILE,"select an image file")
	m(pb,"Quit",PB_EXIT_SCRIPT,"exit script")
	m("write")
	exec(GUI_STRING)

def gui_magic(*c):
	__doc__ = '''\
	Used to speed up coding in gui function that calls it & lines up buttons in one, two or three columns.
	Called with variable length arg list. Uses GUI globals to access list & string data. String in first
	arg changes behavior. If c[0]!="write", adjusts data in lists & if c[0]=="row", current gui block row
	is added to the block, fresh row started & function returns, & if c[0]!="row", arg tuple is added to
	block row & function returns. If c[0]=="write", parses data from GUI_BLOCK with helper functions and
	adds to GUI_STRING.

	GLOBAL VARIABLES:
	GUI_BLOCK -- 2D list for storing gui raw data
	GUI_BLOCK_ROW -- row for GUI_BLOCK
	GUI_STRING -- string later used with exec to draw gui
	
	PARAMETERS:
	c -- charm (variable length, 1st arg changes function behavior)

	OTHER VARIABLES:
	dn -- dictionary name
	xls -- x lists
	wl -- width list
	h -- height
	xc -- x count
	yc -- y count
	r -- row
	ri -- row item
	'''
	global GUI_BLOCK,GUI_BLOCK_ROW,GUI_STRING
	if c[0]!="write":
		if c[0]=="row":
			GUI_BLOCK.append(GUI_BLOCK_ROW)
			GUI_BLOCK_ROW=[]
		else:
			GUI_BLOCK_ROW.append(c)
		return
	else:
		GUI_BLOCK.append(GUI_BLOCK_ROW)
		dn="GUI"
		xls=[[10,235],[10,160,310]]
		wl=[445,220,145]
		h=20
		y=[10+25*i for i in range(len(GUI_BLOCK))]
		y.reverse()
		yc=0
		for r in GUI_BLOCK:
			w=wl[len(r)-1]
			if len(r)==2:x=xls[0]
			else:x=xls[1]
			xc=0
			for ri in r:
				if ri[0]=="text":GUI_STRING+=draw_text(ri[1],x[xc],y[yc])
				elif ri[0]=="colpik":GUI_STRING+=draw_col(dn,ri,x[xc],y[yc],w,h)
				elif ri[0]=="menu":GUI_STRING+=draw_menu(dn,ri,x[xc],y[yc],w,h)
				elif ri[0]=="num":GUI_STRING+=draw_num(dn,ri,x[xc],y[yc],w,h)
				elif ri[0]=="button":GUI_STRING+=draw_button(dn,ri,x[xc],y[yc],w,h)
				elif ri[0]=="tog":GUI_STRING+=draw_tog(dn,ri,x[xc],y[yc],w,h)
				GUI_STRING+="\n"
				xc+=1
			yc+=1

def draw_text(t,x,y):
	__doc__ = '''\
	Returns a string for drawing gui text.

	PARAMETERS:
	t -- text
	x -- x location
	y -- y location
	'''
	y+=4
	s="BGL.glColor3f(1.0,1.0,1.0)\n"
	s+="BGL.glRasterPos2i(%s,%s)\n"%(x,y)
	s+="Draw.Text('%s')"%(t)
	return s

def draw_col(dn,l,x,y,w,h):
	__doc__ = '''\
	Returns a string for drawing a gui color picker.

	PARAMETERS:
	dn -- dictionary name
	l -- list
	x -- x location
	y -- y location
	w -- width
	h -- height
	'''
	s="%s['%s']=Draw.ColorPicker(%s,%s,%s,%s,%s,%s['%s'].val)"%(dn,l[2],l[1],x,y,w,h,dn,l[2])
	return s

def draw_menu(dn,l,x,y,w,h):
	__doc__ = '''\
	Returns a string for drawing a gui menu.

	PARAMETERS:
	dn -- dictionary name
	l -- list
	x -- x location
	y -- y location
	w -- width
	h -- height
	'''
	s="%s['%s']=Draw.Menu('%s',%s,"%(dn,l[3],l[1],l[2])
	s+="%s,%s,%s,%s,%s['%s'].val,'%s')"%(x,y,w,h,dn,l[3],l[4])
	return s


def draw_num(dn,l,x,y,w,h):
	__doc__ = '''\
	Returns a string for drawing a gui number button.

	PARAMETERS:
	dn -- dictionary name
	l -- list
	x -- x location
	y -- y location
	w -- width
	h -- height
	'''
	s="%s['%s']=Draw.Number('%s',%s,%s,%s,%s,%s,"%(dn,l[3],l[1],l[2],x,y,w,h)
	s+="%s['%s'].val,%s,%s,'%s')"%(dn,l[3],l[4],l[5],l[6])
	return s


def draw_button(dn,l,x,y,w,h):
	__doc__ = '''\
	Returns a string for drawing a gui push button.

	PARAMETERS:
	dn -- dictionary name
	l -- list
	x -- x location
	y -- y location
	w -- width
	h -- height
	'''
	return "Draw.PushButton('%s',%s,%s,%s,%s,%s,'%s')"%(l[1],l[2],x,y,w,h,l[3])

def draw_tog(dn,l,x,y,w,h):
	__doc__ = '''\
	Returns a string for drawing a gui menu.

	PARAMETERS:
	dn -- dictionary name
	l -- list
	x -- x location
	y -- y location
	w -- width
	h -- height
	'''
	s="%s['%s']=Draw.Toggle('%s',%s,"%(dn,l[3],l[1],l[2])
	s+="%s,%s,%s,%s,%s['%s'].val,'%s')"%(x,y,w,h,dn,l[3],l[4])
	return s

def event(evt,val):
	__doc__ = '''\
	Ignore keyboard/mouse "down" events. Just processing ESC key.
	'''
	if val:return
	if evt==Draw.QKEY:Draw.Exit()

def bevent(evt):
	__doc__ = '''\
	This is the button event callback.

	GLOBAL VARIABLES:
	CCHS -- color channels
	WRITE_SCRIPT -- write sample script link script (not implemented yet)

	PARAMETERS:
	evt -- the button event integer

	VARIABLES:
	drl -- Draw.Redraw list (does event need a redraw?)
	'''
	global CCHS,WRITE_SCRIPT
	drl=[MU_WIDTH_HEIGHT,MU_MESH_TYPE,TG_MATERIAL_RANGE,NM_X_CONTRAST,NM_Y_CONTRAST,TG_DOTS_EXT_SAME_MAT]
	t,f=True,False
	if evt in drl:
		Draw.Redraw()
	elif evt==MU_RGBA:
		CCHS=[[t,t,t,f],[t,t,t,t],[t,f,f,f],[f,t,f,f],[f,f,t,f],[f,f,f,t]][GUI['RGBA'].val]
	elif evt==PB_SELECT_FILE:
		#get_mesh_image("foo.png")
		Window.ImageSelector(get_mesh_image,"Load Image!")
		Draw.Redraw()
	elif evt==PB_CREATE_MESH:
		WRITE_SCRIPT=False
		make_image_mesh()
	elif evt==PB_MAKE_MESH_N_SCRIPT:
		WRITE_SCRIPT=True
		make_image_mesh()
	elif evt==PB_EXIT_SCRIPT:
		Draw.Exit()

def get_mesh_image(imagename):
	__doc__ = '''\
	Window.ImageSelector needs a callback function. This is it.
	'''
	global MY_IMAGE
	MY_IMAGE=Image.Load(imagename)

def make_material_data(pr):
	__doc__ = '''\
	Prepares material data. Returns two lists. The first is set of RGB color tuples, the second is a set of
	floats for emit levels. If the mesh type is web, we simply return the color and emit data that the user
	set for the first material. If it's a dot mesh, we don't need to do much unless it's for a material
	range. Then we interpolate between the settings for materials 1 and 2. If it's an extruded mesh that
	uses a material range, we interpolate between materials 2 & 3, and we use material 1 for the web area.
	If it's a simple extruded mesh, gui colors are assigned.

	PARAMETERS:
	pr -- user preferences
	 * pr['C0'],pr['C1'],pr['C2'] -- color data
	 * pr['E0'],pr['E1'],pr['E2'] -- emit data
	 * pr['MT'] -- mesh type
	 * pr['MRG'] -- material range (bool)
	 * pr['NM'] -- number of materials

	OTHER VARIABLES:
	cd -- color data
	ed -- emit data
	i,j -- loop/temp variables
	c1,c2,c3 -- colors
	e1,e2,e3 -- emit levels
	'''
	cd=[]
	ed=[]
	c1,c2,c3=pr['C0'],pr['C1'],pr['C2']
	e1,e2,e3=pr['E0'],pr['E1'],pr['E2']
	if pr['MT']==WEB:
		cd=[c1]
		ed=[e1]
	elif pr['MT']==DOTS:
		if pr['MRG']:
			for i in range(pr['NM']):
				j=i/float(pr['NM']-1)
				cd.append((c1[0]*(1-j)+c2[0]*j,c1[1]*(1-j)+c2[1]*j,c1[2]*(1-j)+c2[2]*j))
				ed.append(e1*(1-j)+e2*j)
		else:
			cd=[c1]
			ed=[e1]
	elif pr['MT']==EXTRUDE:
		if pr['MRG']:
			cd.append(c1)
			ed.append(e1)
			for i in range(pr['NM']-1):
				j=i/float(pr['NM']-2)
				cd.append((c2[0]*(1-j)+c3[0]*j,c2[1]*(1-j)+c3[1]*j,c2[2]*(1-j)+c3[2]*j))
				ed.append(e1*(1-j)+e2*j)
		else:
			cd=[c1,c2,c3]
			ed=[e1,e2,e3]
	return cd,ed

def get_animation_image(imagename):
	__doc__ = '''\
	Window.ImageSelector needs a callback function. Used here to get a start image for an animation.
	FOR POSSIBLE LATER USE WITH SCRIPT LINKS.
	'''
	global ANIMATION_IMAGE
	ANIMATION_IMAGE=Image.Load(imagename)

def make_image_mesh():
	__doc__ = '''\
	Coordinates the work of other functions and then creates the mesh. Gets the size of the image in
	pixels. Calls make_grid. Calls a couple functions to get and assign pixel intensity data. Calls
	walk_around to adjust some coordinates. Creates the mesh and assigns data gathered for vertices and
	faces. Does some work with the scene and object. Confusion, but it seems to work. Computes and assigns
	material data.
	'''
	global MY_IMAGE
	editmode=Window.EditMode()
	if editmode:Window.EditMode(0)
	pr=gui_prefs(GUI)
	Window.WaitCursor(1)
	u,v=MY_IMAGE.getSize()
	g,xd,yd,xc,yc,ivl,bvl=make_grid(u,v,pr)
	pg=pixel_intensities(MY_IMAGE,u,v,pr)
	eg=walk_around(g,xd,yd,u,v,pg,pr)
	vpg=map_pixel_data(xc,yc,u,v,pg)
	vs,bvm,evm=make_vertex_data(g,eg,vpg,xd,yd,xc,yc,ivl,bvl,pr)
	faces,ftl,fil=make_face_data(xc,yc,bvm,evm,ivl,vpg,pr)
	me=Mesh.New('ImageMesh')
	me.verts.extend(vs)
	me.faces.extend(faces)
	scn=Scene.GetCurrent()
	scn.objects.selected=[]
	ob=scn.objects.new(me,'ImageMeshObj')
	cd,ed=make_material_data(pr)
	tmp=[]
	for i in range(len(cd)):
		ts="ImageMeshMat%02d" %(i)
		im_mat=Material.New(ts)
		im_mat.rgbCol=cd[i]
		im_mat.emit=ed[i]
		tmp.append(im_mat)
	me.materials=tmp
	assign_mats_to_faces(me.faces,ftl,fil,pr)
	Window.WaitCursor(0)
	if WRITE_SCRIPT:
		pass
	if editmode:Window.EditMode(1)

def gui_prefs(gui_dict):
	__doc__ = '''\
	Just makes a simple dictionary from gui data.
	'''
	simple_dict={}
	for i in gui_dict:
		simple_dict[i]=gui_dict[i].val
	return simple_dict

def assign_mats_to_faces(faces,ftl,fil,pr):
	__doc__ = '''\
	Uses face data to assign materials. If the mesh type is web, or dots witout a material range, all faces
	are assigned the first material. If it's a simple extruded mesh, assignments are made using face type
	categories. If material ranges are used with dots or extrusions, face intensity data is used for the
	assignments.

	PARAMETERS:
	faces -- list of mesh faces
	ftl -- face type list
	fil -- face intensity list
	pr -- user preferences
	 * pr['MT'] -- mesh type
	 * pr['MRG'] -- material range (bool)
	 * pr['DE'] -- same mat for dots and extrusion (bool)
	 * pr['NM'] -- number of materials

	OTHER VARIABLES:
	il -- intensity list
	i,j -- loop variables
	'''
	if pr['MT']==WEB:
			for i in range(len(faces)):
				faces[i].mat=0
	elif pr['MT']==DOTS:
		if pr['MRG']:
			il=[(i+1)/float(pr['NM']) for i in range(pr['NM'])]
			for i in range(len(faces)):
				for j in range(len(il)):
					if fil[i]<=il[j]:
						faces[i].mat=j
						break
		else:
			for i in range(len(faces)):
				faces[i].mat=0
	elif pr['MT']==EXTRUDE:
		if pr['MRG']:
			il=[(i+1)/float(pr['NM']-1) for i in range(pr['NM']-1)]
			for i in range(len(faces)):
				if ftl[i]==1:
					faces[i].mat=0
				elif ftl[i]==3 and not pr['DE']:
					faces[i].mat=0
				else:
					for j in range(len(il)):
						if fil[i]<=il[j]:
							faces[i].mat=j+1
							break
		else:
			for i in range(len(faces)):
				if ftl[i]==1:faces[i].mat=0
				elif ftl[i]==3:faces[i].mat=1
				elif ftl[i]==2:faces[i].mat=2

def make_grid(u,v,pr):
	__doc__ = '''\
	Creates mesh grid data based on image pixel count and user settings. If the image size is x by y, the
	grid created will have 3x+2 by 3y+2 vertices. Still 2D only. After determining the number of vertices,
	distances between vertices in both x and y directions is calculated. The first step is to check if the
	user decided to set just the width, just the height or both. If not both, distances between verts will
	be the same in both directions. This is not always true if user decided to set all. Set variables for
	half the width and half the height that will be used in the loop. Create the grid as a list of empty
	lists. Within loop, we determine the x and y locations of each vertex. Other functions will move the
	locations of some of these vertices. We don't worry about that here. We gather additional information
	about the vertices within the loop. There are three types of vertices within the grid: primary (image),
	secondary and perimeter (border). A primary vertex is one that was mapped directly from an image pixel.
	A secondary is one of 8 vertices that surround a primary. Together they form a 3 by 3 sub-grid with the
	primary in the center. Perimeter vertices are positioned along the four edges of the grid.

	PARAMETERS:
	u -- u in image uv
	v -- v in image uv
	pr -- user preferences
	 * pr['WOH'] -- width or height setting (1:"width", 2:"height", 3:"width and height")
	 * pr['W'] -- width value (1.0 - 24.0)
	 * pr['H'] -- height value (1.0 - 24.0)

	VARIABLES:
	xc -- x count (converting from image pixels to 2D grid vertices)
	yc -- y count (converting from image pixels to 2D grid vertices)
	xd -- x distance between adjacent vertices
	yd -- y distance between adjacent vertices
	hw -- half width (of grid)
	hh -- half height (of grid)
	g -- grid
	x,y -- loop variables
	ivl -- image vert list (bools, True for verts mapped from image pixels)
	bvl -- border vert list (bools, True for verts on perimeter of the grid)
	'''
	xc=u*3+2
	yc=v*3+2
	if pr['WOH']==1:
		hw=pr['W']/2.0
		xd=yd=pr['W']/float(xc-1)
		hh=yd*(yc-1)/2.0
	elif pr['WOH']==2:
		hh=pr['H']/2.0
		xd=yd=pr['H']/float(yc-1)
		hw=xd*(xc-1)/2.0
	else:
		hw=pr['W']/2.0
		xd=pr['W']/float(xc-1)
		hh=pr['H']/2.0
		yd=pr['H']/float(yc-1)
	g=[[]for x in range(xc)]
	ivl=copy.deepcopy(g)
	bvl=copy.deepcopy(g)
	for x in range(xc):
		for y in range(yc):
			g[x].append([x*xd-hw,y*yd-hh])
			if x%3==2 and y%3==2:ivl[x].append(True)
			else:ivl[x].append(False)
			if x in[0,xc-1]or y in[0,yc-1]:bvl[x].append(True)
			else:bvl[x].append(False)
	return g,xd,yd,xc,yc,ivl,bvl

def walk_around(g,xd,yd,u,v,pg,pr):
	__doc__ = '''\
	This function will alter the positions of the secondary vertices. Walk lists are created for walking
	around to the secondary verticies surrounding the image vertices. If the mesh type is extrusion, an
	extrusion grid will store vertex data using the taper setting. For diamond primatives, a denominator
	list is created. For octagons, trig data is used. Within the loops, which index through the dimensions
	of the original image, the pixel intensity is used with globals to set the distance from primary
	vertices. The image index variables are multiplied by 3 and added to 2 to give corresponding grid
	locations. The inner loop walks around and adjust the locations of the vertices. We're still only
	concerned with 2D at this point.

	PARAMETERS:
	g -- grid
	xd -- x distance between vertices (standard)
	yd -- y distance between vertices (standard)
	u -- width (image)
	v -- height (image)
	pg -- pixel grid
	pr -- user preferences
	 * pr['MT'] -- mesh type
	 * pr['PP'] -- primative pattern (1:"square", 2:"diamond", 3:"octagon")
	 * pr['XCO'] -- x contrast
	 * pr['XBR'] -- x brightness
	 * pr['YCO'] -- y contrast
	 * pr['YBR'] -- y brightness
	 * pr['ET'] -- extrusion taper

	OTHER VARIABLES:
	xw -- x walk list for indexing around image vertex (possible values:-1,0,1)
	yw -- y walk list for indexing around image vertex (possible values:-1,0,1)
	eg -- extrusion grid
	dl -- denominator list
	cl -- cosine list
	sl -- sine list
	ui -- u index in image uv
	vi -- v index in image uv
	pxi -- image intensity
	x -- x element in grid corresponding to ui in image
	y -- y element in grid corresponding to vi in image
	xl -- x location of a primary (image) vertex
	yl -- y location of a primary (image) vertex
	ox -- offset x
	oy -- offset y
	dfcx -- distance from center x (relative to an image vertex)
	dfcy -- distance from center y (relative to an image vertex)
	dx -- delta x
	dy -- delta y
	'''
	xw=[1,1,0,-1,-1,-1,0,1]
	yw=[0,1,1,1,0,-1,-1,-1]
	if pr['MT']==EXTRUDE:eg=copy.deepcopy(g)
	else:eg=False
	if pr['PP']==2:dl=[1,2,1,2,1,2,1,2]
	elif pr['PP']==3:cl,sl=get_trig_data()
	for ui in range(u):
		for vi in range(v):
			pxi=pg[ui][vi]
			dfcx=xd*(AMN+DA*(pr['XBR']*(1-pr['XCO'])+pr['XCO']*pxi))
			dfcy=yd*(AMN+DA*(pr['YBR']*(1-pr['YCO'])+pr['YCO']*pxi))
			x=ui*3+2
			y=vi*3+2
			xl,yl=g[x][y][0],g[x][y][1]
			for i in range(8):
				ox,oy=xw[i],yw[i]
				if pr['PP']==1:dx,dy=ox*dfcx,oy*dfcy
				elif pr['PP']==2:dx,dy=ox*dfcx/dl[i],oy*dfcy/dl[i]
				elif pr['PP']==3:dx,dy=cl[i]*dfcx,sl[i]*dfcy
				g[x+ox][y+oy][0]=xl+dx
				g[x+ox][y+oy][1]=yl+dy
				if pr['MT']==EXTRUDE:
					eg[x+ox][y+oy][0]=xl+dx*pr['ET']
					eg[x+ox][y+oy][1]=yl+dy*pr['ET']
	return eg

def get_trig_data():
	__doc__ = '''\
	Gathers trig data to be used when making octagons and returns two eight item tuples. The first
	represents the cosine values of all the angles that are multiples of 45 degrees. The second is for the
	sines. Note that cos(pi/4) = sin(pi/4).
	'''
	c=s=math.cos(math.pi/4.0)
	return (1.0,c,.0,-c,-1.0,-c,.0,c),(.0,s,1.0,s,.0,-s,-1.0,-s)

def pixel_intensities(image,u,v,pr):
	__doc__ = '''\
	Grabs & modifies pixel intensity data with user settings for reverse & color channels.

	PARAMETERS:
	image -- the image object
	u -- u in image uv
	v -- v in image uv
	pr -- user preferences
	 * pr['RV'] -- reverse intensity setting

	GLOBAL VARIABLES:
	  PROBABLY SHOULD USE PARAMETERS INSTEAD FOR EASE OF USE WITH IMPORTS AND SCRIPT LINKS
	CCHS --  bools for each of the RGBA channels to determine which are used
	
	OTHER VARIABLES:
	x,y,i -- index variables
	c -- color
	count -- a counter
	pg -- pixel grid
	pxi -- pixel intensity
	'''
	pg=[[]for x in range(u)]
	for x in range(u):
		for y in range (v):
			c=image.getPixelF(x,y)
			pxi=.0
			count=.0
			for i in range(4):
				if CCHS[i]:
					pxi+=c[i]
					count+=1.0
			pxi/=count
			if pr['RV']:pxi=1.0-pxi
			pg[x].append(pxi)
	return pg

def map_pixel_data(xc,yc,u,v,pg):
	__doc__ = '''\
	Maps pixel intensities from image size to larger vertex grid size. First, create 2D vertex pixel grid
	& fill with junk. Loop through image pixels & multiply by 3 for grid. Inner loops make 3X3 subgrid for
	each pixel.
	
	PARAMETERS:
	xc -- x count
	yc -- y count
	u -- u in image uv
	v -- v in image uv
	pg -- pixel grid
		
	OTHER VARIABLES:
	ui,vi -- loop variables
	x,y -- loop/multiplier variables
	xd,yd -- x delta & y delta (for subgrid)
	vpg -- vertex pixel grid
	'''
	vpg=[[0 for y in range(yc)]for x in range(xc)]
	for ui in range(u):
		x=ui*3
		for vi in range(v):
			y=vi*3
			for xd in range(1,4):
				for yd in range(1,4):
					vpg[x+xd][y+yd]=pg[ui][vi]
	return vpg

def make_vertex_data(g,eg,vpg,xd,yd,xc,yc,ivl,bvl,pr):
	__doc__ = '''\
	Returns the list of vertices. It also returns a couple of mapping lists that are used later to make
	sure that each face is built with the the correct vertices. Get the average between x and y distances.
	Create an empty list to be filled with vertices. Next, all elements of two 2D mapping lists are
	populated with -1, and a counter is set to 0. In the loops, the type of mesh is determined. If it's an
	extrusion, extra vertices are created.

	PARAMETERS:
	g -- grid
	eg -- extrusion grid
	vpg -- vertex pixel grid
	xd -- x distance
	yd -- y distance
	xc -- x count
	yc -- y count
	ivl -- image vertex list
	bvl -- border vertex list
	pr -- user preferences
	 * pr['MT'] -- mesh type
	 * pr['IE'] -- intensity extrusion
	 * pr['EXDS'] -- extrusion distance
	 * pr['EXDR'] -- extrusion direction

	OTHER VARIABLES:
	ad -- average distance for extruding along z (between x & y)
	vs -- vertices
	bvm -- base vertex map (from grid locations to real vertices)
	evm -- extruded vertex map (from grid locations to real vertices)
	count -- a counter
	x,y -- looping variables
	f -- flag
	z -- z distance
	'''
	ad=xd+yd/2.0
	vs=[]
	bvm=[[-1 for y in range(yc)]for x in range(xc)]
	evm=copy.deepcopy(bvm)
	count=0
	for x in range(xc):
		for y in range(yc):
			if pr['MT']==WEB:f=not ivl[x][y]
			elif pr['MT']==DOTS:f=not bvl[x][y]
			elif pr['MT']==EXTRUDE:
				f=not ivl[x][y]
				if not bvl[x][y]:
					if pr['IE']:z=ad*(AMN+10*vpg[x][y]*pr['EXDS'])
					else:z=ad*(AMN+10*pr['EXDS'])
					if pr['EXDR']:z=-z
					vs.append([eg[x][y][0],eg[x][y][1],z])
					evm[x][y]=count
					count+=1
			if f:
				vs.append([g[x][y][0],g[x][y][1],0])
				bvm[x][y]=count
				count+=1
	return vs,bvm,evm

def make_face_data(xc,yc,bvm,evm,ivl,vpg,pr):
	__doc__ = '''\
	Makes & returns face data. If mesh type is extrusion, walk lists filled for face loops. Empty lists
	created for faces, face types & face intensities. All are filled in nested loops. Note that the base
	vertex map will have different data depending on mesh type.
	
	PARAMETERS:
	xc -- x count
	yc -- y count
	bvm -- base vertex map (from grid locations to real vertices)
	evm -- extruded vertex map (from grid locations to real vertices)
	ivl -- image vertex list
	vpg -- vertex pixel grid
	pr -- user preferences
	 * pr['MT'] -- mesh type

	OTHER VARIABLES:
	xw -- x walk
	yw -- y walk
	faces -- face data
	ftl -- face type list (1:"z base", 2:"z transformed", 3:"stretched between")
	fil -- face intesity list
	x,y,i -- loop variables
	'''
	if pr['MT']==EXTRUDE:xw,yw=[1,1,0,-1,-1,-1,0,1,1],[0,1,1,1,0,-1,-1,-1,0]
	faces=[]
	ftl=[]
	fil=[]
	for x in range(xc-1):
		for y in range(yc-1):
			if pr['MT']==DOTS:
				if True not in[ivl[x][y],ivl[x+1][y],ivl[x+1][y+1],ivl[x][y+1]]:
					continue
			elif pr['MT']==EXTRUDE:
				if True in [ivl[x][y],ivl[x+1][y],ivl[x+1][y+1],ivl[x][y+1]]:
					v=[evm[x][y],evm[x+1][y],evm[x+1][y+1],evm[x][y+1]]
					if -1 not in v:
						faces.append(v)
						ftl.append(2)
						fil.append(vpg[x][y])
			v=[bvm[x][y],bvm[x+1][y],bvm[x+1][y+1],bvm[x][y+1]]
			if -1 not in v:
				faces.append(v)
				ftl.append(1)
				fil.append(vpg[x][y])
	if pr['MT']==EXTRUDE:
		for x in range(xc):
			for y in range(yc):
				if ivl[x][y]:
					for i in range(8):
						x1,x2,y1,y2=x+xw[i],x+xw[i+1],y+yw[i],y+yw[i+1]
						v=[evm[x2][y2],evm[x1][y1],bvm[x1][y1],bvm[x2][y2]]
						faces.append(v)
						ftl.append(3)
						fil.append(vpg[x][y])
	return faces,ftl,fil

if __name__=="__main__":
	# main()
	Draw.Register(gui,event,bevent)



