# Python script to read a meshwork file
# and spit out POV-Ray or Lightspeed3D source code.

import string
import math

gVertices = []
gEdges = []
gTriangles = []
gTriNormals = []
gVNormals = []

gConnectedTo = []

def Cross(v1,v2):
	return [ \
		v1[1]*v2[2] - v1[2]*v2[1],
		v1[2]*v2[0] - v1[0]*v2[2],
		v1[0]*v2[1] - v1[1]*v2[0] ]

def Dot(v1,v2):
	return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]

def Diff(v1,v2):
	return [ v2[0]-v1[0], v2[1]-v1[1], v2[2]-v1[2] ]

def ComputeBoundingBox():
	minp=[9999,9999,9999]
	maxp=[-9999,-9999,-9999]
	for p in gVertices:
		for i in range(0,3):
			if p[i] > maxp[i]: maxp[i] = p[i]
			if p[i] < minp[i]: minp[i] = p[i]
	return minp, maxp

def Normalize(v):
	mag = math.sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2])
	out = [0,0,0]
	for i in range(0,3): out[i] = v[i] / mag
	return out
	
def ComputeTriNormals():
	for t in gTriangles:
		# find normal for triangle t
		v1 = Diff(gVertices[t[1]],gVertices[t[0]])
		v2 = Diff(gVertices[t[2]],gVertices[t[1]])
		gTriNormals.append( Cross( Normalize(v1), Normalize(v2) )	)

def ComputeVertexNormals():
	if not gTriNormals: ComputeTriNormals()
	for i in range(0, len(gVertices)):
		# find average of triangle normals for all adjacent triangles
		sum = [0,0,0]
		count = 0
		for tnum in range(0, len(gTriangles)):
			t = gTriangles[tnum]
			if t[0] == i or t[1] == i or t[2] == i:
				for idx in range(0,3): sum[idx] = sum[idx] + gTriNormals[tnum][idx]
			count = count + 1
		gVNormals.append( [sum[0]/count, sum[1]/count, sum[2]/count] )

def NoteTriangle(v1,v2,v3):
	lst = [v1,v2,v3]
	lst.sort()
	# sort the vertices so that the normal points towards the front (-z)
	v1 = Diff(gVertices[lst[1]],gVertices[lst[0]])
	v2 = Diff(gVertices[lst[2]],gVertices[lst[1]])
	crossprod = Cross( v1, v2 )	
#	print v1, v2, crossprod, Dot(crossprod, [0,0,-1])
	if Dot(crossprod, [0,0,-1]) < 0:
		lst = [lst[1],lst[0],lst[2]]
	if lst not in gTriangles:
		gTriangles.append(lst)

def FindTriangles():
	# first, find the connection set of each vertex
	gConnectedTo = map(lambda x:[], range(0,len(gVertices)))
	for e in gEdges:
		gConnectedTo[e[0]].append(e[1])
		gConnectedTo[e[1]].append(e[0])
	
	# now, find triangles
	for v1 in range(0,len(gVertices)):
		for v2 in gConnectedTo[v1]:
			for v3 in gConnectedTo[v2]:
				if v1 in gConnectedTo[v3]:
					NoteTriangle(v1, v2, v3)


def ReadFile(fname):
	file = open(fname)
	lines = map(lambda x:string.strip(x),file.readlines())
	file.close()
	
	ln = 0
	while lines[ln] != "VERTICES": ln = ln+1
	ln = ln+1
	
	while lines[ln] != "EDGES":
		parts = string.split(lines[ln])
		nparts = map(lambda x:string.atof(x), parts)
		gVertices.append(tuple(nparts[1:]))
		ln = ln+1
	
	print len(gVertices), "vertices"

	ln = ln+1
	while lines[ln] != "END":
		nparts = map(lambda x:string.atoi(x), string.split(lines[ln]))
		gEdges.append(tuple(nparts))	
		ln = ln+1

	print len(gEdges), "edges"

	FindTriangles()
	print len(gTriangles), "triangles"

def PovPoint(vnum):
	return "<" + string.join(map(lambda x:str(x),gVertices[vnum]),',') + ">"
	
def WritePov():
	for t in gTriangles:
		print "  triangle { %s, %s, %s }" % (PovPoint(t[0]), PovPoint(t[1]), PovPoint(t[2]))

def L3DPoint(vnum):
	return "Vec3D("+ string.join(map(lambda x:str(x),gVertices[vnum]),',') + ")"

def WriteL3D():
	for t in gTriangles:
		print "  out->AddTri( %s, %s, %s );" % (L3DPoint(t[0]), L3DPoint(t[1]), L3DPoint(t[2]))

def WriteM3D():
	print "\tTQ3Point3D points[qtyPts] = {"
	for p in gVertices:
		print "\t\t%s, %s, %s" % tuple(p),
		if p != gVertices[-1]: print ","
		else: print
	print "\t};\n\n\tTQ3TriMeshTriangleData triangles[qtyTris] = {"
	for t in gTriangles:
		print "\t\t%s, %s, %s" % tuple(t),
		if t != gTriangles[-1]: print ","
		else: print
	print "\t};"

def Write3DMF():
	# print header... including major version (1), minor version (6), flag (Normal),
	# and pointer to table of contents -- which need not actually exist
	print "3DMetafile ( 1 6 Normal tableofcontents0> )"

	# print name of the trimesh object
	print "\tmeshworkObj1:"

	# make a container to enclose the TriMesh with its attribute arrays
	print "\tContainer ("

	# start the trimesh data structure
	print "\t\tTriMesh ("

	# print nums, etc.
	# NOTE: for now, we're not storing edges...
	# but it might be a good way for Meshwork to store this info for itself!
	print "\t\t\t%d %d %d %d %d %d # numTriangles numTriangleAttributeTypes numEdges numEdgeAttributeTypes numPoints numVertexAttributeTypes" \
		% (len(gTriangles), 1, 0, 0, len(gVertices), 1)

	# output all the triangle vertices
	print "\n\t\t\t# triangles"
	for t in gTriangles:
		print "\t\t\t%d %d %d" % tuple(t)

	# output all the triangle vertices
	print "\n\t\t\t# points"
	for p in gVertices:
		print "\t\t\t%f %f %f" % tuple(p)

	# print out the bounding box
	minp, maxp = ComputeBoundingBox()
	print "\n\t\t\t%f %f %f  %f %f %f False" % tuple( minp + maxp )

	# close the trimesh data structure
	print "\t\t)\n"
	
	# print out the first attribute set -- triangle normals
	print "\t\tAttributeArray ("
	print "\t\t\t3",		# 3 == kQ3AttributeTypeNormal
	print 0,				# reserved
	print 0,				# 0 == triangle attribute, 1 == edge, 2 == vertex
	print 0,				# position in attribute array (0-based)
	print 0					# 0 == no custom attributes, 1 == custom
	print "\t\t\t# triangle normals"
	if not gTriNormals: ComputeTriNormals()
	for n in gTriNormals:
		print "\t\t\t%f %f %f" % tuple(n)
	print "\t\t)\n"

	# print out the next attribute set -- vertex normals
	print "\t\tAttributeArray ("
	print "\t\t\t3",		# 3 == kQ3AttributeTypeNormal
	print 0,				# reserved
	print 2,				# 0 == triangle attribute, 1 == edge, 2 == vertex
	print 0,				# position in attribute array (0-based)
	print 0					# 0 == no custom attributes, 1 == custom
	print "\t\t\t# triangle normals"
	if not gVNormals: ComputeVertexNormals()
	for n in gVNormals:
		print "\t\t\t%f %f %f" % tuple(n)
	print "\t\t)\n"
	
	# close the container
	print "\t)"
	
	
	
ReadFile("cylinder.mesh")
print
#WritePov()
#WriteL3D()
#WriteM3D()
Write3DMF()
print
raw_input("...")
