from __future__ import print_function
import numpy as np

class Obj:
	def __init__(self, fn):
		self.ind_v  = 0
		self.ind_vt = 0
		self.ind_vn = 0
		self.fn = fn
		self.out = open(fn + ".tmp", "w")
		self.out.write("mtllib dinnerware.mtl\n")
	def __del__(self):
		self.out.close()
		import shutil
		shutil.move(self.fn + ".tmp", self.fn)
	def push_v(self, v):
		self.out.write("v %f %f %f\n" % (v[0],v[1],v[2]))
		self.ind_v += 1
		return self.ind_v
	def push_vt(self, vt):
		self.out.write("vt %f %f\n" % (vt[0],vt[1]))
		self.ind_vt += 1
		return self.ind_vt
	def push_vn(self, vn):
		vn /= np.linalg.norm(vn)
		self.out.write("vn %f %f %f\n" % (vn[0],vn[1],vn[2]))
		self.ind_vn += 1
		return self.ind_vn


def convex_hull(points, vind, nind, tind, obj):
	"super ineffective"
	cnt = len(points)
	for a in range(cnt):
		for b in range(a+1,cnt):
			for c in range(b+1,cnt):
				vec1 = points[a] - points[b]
				vec2 = points[a] - points[c]
				n  = np.cross(vec1, vec2)
				n /= np.linalg.norm(n)
				C = np.dot(n, points[a])
				inner = np.inner(n, points)
				pos = (inner <= C+0.0001).all()
				neg = (inner >= C-0.0001).all()
				if not pos and not neg: continue
				obj.out.write("f %i//%i %i//%i %i//%i\n" % ( 
					(vind[a], nind[a], vind[b], nind[b], vind[c], nind[c])
					if (inner - C).sum() < 0 else
					(vind[a], nind[a], vind[c], nind[c], vind[b], nind[b]) ) )
				#obj.out.write("f %i/%i/%i %i/%i/%i %i/%i/%i\n" % ( 
				#	(vind[a], tind[a], nind[a], vind[b], tind[b], nind[b], vind[c], tind[c], nind[c])
				#	if (inner - C).sum() < 0 else
				#	(vind[a], tind[a], nind[a], vind[c], tind[c], nind[c], vind[b], tind[b], nind[b]) ) )

def test_convex_hull():
	obj = Obj("convex_test.obj")
	vlist = np.random.uniform( low=-0.1, high=+0.1, size=(100,3) )
	nlist = vlist.copy()
	tlist = np.random.uniform( low=0, high=+1, size=(100,2) )
	vind = [obj.push_v(xyz) for xyz in vlist]
	nind = [obj.push_vn(xyz) for xyz in nlist]
	tind = [obj.push_vt(uv) for uv in tlist]
	convex_hull(vlist, vind, nind, tind, obj)

class Contour:
	def __init__(self):
		self.vprev_vind = None

	def f(self, obj, vlist_vind, vlist_tind, vlist_nind):
		cnt = len(vlist_vind)
		for i1 in range(cnt):
			i2 = i1-1
			obj.out.write("f %i/%i/%i %i/%i/%i %i/%i/%i\n" % ( 
				vlist_vind[i2], vlist_tind[i2], vlist_nind[i2],
				vlist_vind[i1], vlist_tind[i1], vlist_nind[i1],
				self.vprev_vind[i1], self.vprev_tind[i1], self.vprev_nind[i1],
				) )
			obj.out.write("f %i/%i/%i %i/%i/%i %i/%i/%i\n" % ( 
				vlist_vind[i2], vlist_tind[i2], vlist_nind[i2],
				self.vprev_vind[i1], self.vprev_tind[i1], self.vprev_nind[i1],
				self.vprev_vind[i2], self.vprev_tind[i2], self.vprev_nind[i2],
				) )

	def belt(self, obj, vlist, nlist, tlist):
		vlist_vind = [obj.push_v(xyz)  for xyz in vlist]
		vlist_tind = [obj.push_vt(xyz) for xyz in tlist]
		vlist_nind = [obj.push_vn(xyz) for xyz in nlist]
		if self.vprev_vind:
			self.f(obj, vlist_vind, vlist_tind, vlist_nind)
		else:
			self.first_vind = vlist_vind
			self.first_tind = vlist_tind
			self.first_nind = vlist_nind
		self.vprev_vind = vlist_vind
		self.vprev_tind = vlist_tind
		self.vprev_nind = vlist_nind

	def finish(self, obj):
		self.f(obj, self.first_vind, self.first_tind, self.first_nind)

def test_contour():
	RAD1 = 2.0
	RAD2 = 1.5
	obj = Obj("torus.obj")
	obj.out.write("usemtl porcelain\n")
	contour = Contour()
	for step in range(100):
		angle = step/100.0*2*np.pi
		belt_v = []
		belt_n = []
		belt_t = []
		for b in range(50):
			beta = b/50.0*2*np.pi
			r = RAD2*np.cos(beta) + RAD1
			z = RAD2*np.sin(beta)
			belt_v.append( np.array( [
				np.cos(angle)*r,
				np.sin(angle)*r,
				z] ) )
			belt_n.append( np.array( [
				np.cos(angle)*np.cos(beta),
				np.sin(angle)*np.cos(beta),
				np.sin(beta)] ) )
			belt_t.append( (0,0) )
		contour.belt(obj, belt_v, belt_n, belt_t)
	contour.finish(obj)

#test_convex_hull()
#test_contour()

class RotationFigureParams:
	pass

def generate_plate(p, obj, collision_prefix):
	contour = Contour()
	belt_vlist_3d_prev = None

	for step in range(p.N_VIZ+1):
		angle = step/float(p.N_VIZ)*2*np.pi

		if step % p.COLLISION_EVERY == 0:
			vlist_3d = []
			for x,y in p.belt_simple:
				vlist_3d.append( [
					np.cos(angle)*x*1.06,
					np.sin(angle)*x*1.06,
					y
					] )
			if belt_vlist_3d_prev:
				obj2 = Obj(collision_prefix % (step / p.COLLISION_EVERY))
				obj2.out.write("usemtl pan_tefal\n")
				vlist = np.array( vlist_3d + belt_vlist_3d_prev )
				vlist[len(vlist_3d):] *= 1.01  # break points on one plane
				vlist[0,0:2] += 0.01*vlist[len(vlist_3d),0:2]
				vlist[len(vlist_3d),0:2] += 0.01*vlist[0,0:2]
				nlist = np.random.uniform( low=-1, high=+1, size=vlist.shape )
				tlist = np.random.uniform( low=0, high=+1, size=(len(vlist),2) )
				vind = [obj2.push_v(xyz) for xyz in vlist]
				nind = [obj2.push_vn(xyz) for xyz in nlist]
				convex_hull(vlist, vind, nind, None, obj2)
			belt_vlist_3d_prev = vlist_3d
		if step==p.N_VIZ: break

		belt_v = []
		belt_n = []
		belt_t = []
		for x,y,nx,ny in p.belt:
			belt_v.append( np.array( [
				np.cos(angle)*x,
				np.sin(angle)*x,
				y
				] ) )
			belt_n.append( np.array( [
				np.cos(angle)*nx,
				np.sin(angle)*nx,
				ny
				] ) )
			if ny-nx >= 0:
				belt_t.append( (
					127.0/512 + np.cos(angle)*x/p.RAD_HIGH*105/512,
					(512-135.0)/512 + np.sin(angle)*x/p.RAD_HIGH*105/512) )
			else:
				belt_t.append( (
					382.0/512 + np.cos(angle)*x/p.RAD_HIGH*125/512,
					(512-380.0)/512 + np.sin(angle)*x/p.RAD_HIGH*125/512) )
		contour.belt(obj, belt_v, belt_n, belt_t)

	contour.finish(obj)

def tefal():
	p = RotationFigureParams()
	p.RAD_LOW  = 0.240/2
	p.RAD_HIGH = 0.255/2
	p.H   = 0.075
	p.THICK = 0.005
	p.N_VIZ = 30
	p.COLLISION_EVERY = 5
	p.belt = [
		(p.RAD_HIGH-p.THICK, p.H,     -1,0),  # x y norm
		(p.RAD_HIGH      , p.H,      0,1),
		(p.RAD_HIGH+p.THICK, p.H,     +1,0),
		(p.RAD_LOW+p.THICK, p.THICK,   +1,0),
		(p.RAD_LOW      , 0,       0,-1),
		(            0, 0,       0,-1),
		(            0, p.THICK,   0,1),
		(p.RAD_LOW-p.THICK, p.THICK,   0,1),
		(p.RAD_LOW-p.THICK, 3*p.THICK,-1,0),
		]
	p.belt.reverse()
	p.belt_simple = [
		(p.RAD_HIGH-p.THICK, p.H),
		(p.RAD_HIGH+p.THICK, p.H),
		(p.RAD_LOW       , 0),
		(p.RAD_LOW-p.THICK , 0)
		]
	obj = Obj("pan_tefal.obj")
	obj.out.write("usemtl pan_tefal\n")
	generate_plate(p, obj, "pan_tefal-collision%02i.obj")

def plate():
	p = RotationFigureParams()
	p.RAD_LOW  = 0.110/2
	p.RAD_HIGH = 0.190/2
	p.H   = 0.060
	p.THICK = 0.003
	p.N_VIZ = 30
	p.COLLISION_EVERY = 5
	p.belt = [
		(p.RAD_HIGH-p.THICK, p.H,     -0.9,0.5),  # x y norm
		(p.RAD_HIGH      , p.H,      0,1),
		(p.RAD_HIGH+p.THICK, p.H,     +1,0),
		(p.RAD_LOW+p.THICK, p.THICK,   +1,0),
		(p.RAD_LOW      , 0,       0,-1),
		(            0, 0,       0,-1),
		(            0, p.THICK,   0,1),
		(p.RAD_LOW-3*p.THICK, p.THICK,   0,1),
		(p.RAD_LOW-p.THICK, 3*p.THICK,-0.5,1.0),
		]
	p.belt.reverse()
	p.belt_simple = [
		(p.RAD_HIGH-p.THICK, p.H),
		(p.RAD_HIGH+p.THICK, p.H),
		(p.RAD_LOW       , 0),
		(p.RAD_LOW-p.THICK , 0)
		]
	obj = Obj("plate.obj")
	obj.out.write("usemtl solid_color\n")
	generate_plate(p, obj, "plate-collision%02i.obj")

plate()


