=begin
Copyright 2013 TIG (c)

Permission to use, copy, modify, and distribute this software for           
any purpose and without fee is hereby granted, provided that the       
copyright notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

Name: TriangulateAllFaces.rb

Usage: Plugins > "TriangulateAllFaces"
Processes all Selected Faces: Triangulating them, with a central split. 
Non-Tris are Triangulated then those Tris are split.
If there's no Selection then ALL Faces in the Current Context are processed. 
Dialog asks for Bisect-Angle or Centroid mode.
Undoes in one step.

Version:
1.0 20131017 First issue.
1.1 20131018 Triangulation improved. Triangles split 'centrally'.
1.2 20131020 Now dialog choice of Bisect-Angle or Centroid modes.
=end
###
require 'sketchup.rb'
###
module TIG
module TIG::TriangulateAllFaces
  unless file_loaded?(__FILE__)
	UI.menu("Plugins").add_item("TriangulateAllFaces"){self.new()}
  end#if
  file_loaded(__FILE__)
  ###
  def self.triangulate(face)
	 ### if it's already a triangle leave alone...
	 if face.vertices[3] ### quad or more complex NOT triangle
	  mesh=face.mesh(3)
	  ents=face.parent.entities
	  mat=face.material
	  bac=face.back_material
	   # This is because we need four points, and some times a face only has three vertices.
	   # And we also get the wrong result if any of the first four vertices are co-linear.
	   samples = []
	   samples << face.vertices[0].position             # 0,0 | Origin
	   samples << samples[0].offset(face.normal.axes.x) # 1,0 | Offset Origin in X
	   samples << samples[0].offset(face.normal.axes.y) # 0,1 | Offset Origin in Y
	   samples << samples[1].offset(face.normal.axes.y) # 1,1 | Offset X in Y
	   ### get uvs
	   tw=Sketchup.create_texture_writer
	   # (TT)
	   uv_helper=face.get_UVHelper(true,true,tw) # need one UV Helper
	   xyz = []
	   uv_f = []
	   uv_b = []
	   samples.each{|position|
		  # XYZ 3D coordinates
		  xyz << position
		  # UV 2D coordinates
		  # Front
		  uvq = uv_helper.get_front_UVQ(position)
		  uv_f << self.flattenUVQ(uvq)
		  # Back
		  uvq = uv_helper.get_back_UVQ(position)
		  uv_b << self.flattenUVQ(uvq)
	   }
	  ###
	  grp=ents.add_group()
	  grp.entities.fill_from_mesh(mesh, true, 0)
	  # Adds the mesh without soft/smooth edges. (and faster)
	  grp.entities.grep(Sketchup::Face).each{|e|
		next unless e.valid?
		if mat # (TT) # Position texture.
		  pts = []
		  (0..3).each{|i|
			 pts << xyz[i]
			 pts << uv_f[i]
		  }
			e.position_material(mat,pts, true)
		end#if
		if bac # (TT) # Position texture.
		  pts = []
		  (0..3).each { |i|
			 pts << xyz[i]
			 pts << uv_b[i]
		  }
		   e.position_material(bac, pts, false)
		end#if
	  }
	  ### force intersection of all triangles so if any 4 sided they are fixed.
	  grp.entities.intersect_with(true, grp.transformation, grp.entities, grp.transformation, true, grp.entities.to_a)
	  ### fix 4 sided triangles...
	  grp.entities.grep(Sketchup::Face).each{|e|
		next unless e.valid?
		verts=e.vertices
		if verts.length > 3
		  verts.each{|v|
			   (verts-[v]).each{|vv|
					grp.entities.add_line(v.position, vv.position)
				}
			}
		end#if
	  }
	  face.erase! if face.valid?
	  grp.explode if grp.valid?
	 end#if
	end#def
	# Get UV coordinates from UVQ matrix
	def self.flattenUVQ(uvq)
	  return Geom::Point3d.new(uvq.x / uvq.z, uvq.y / uvq.z, 1.0)
	end
	###
  def self.new()
	model = Sketchup.active_model
	ents = model.active_entities
	sel = model.selection
	faces = sel.grep(Sketchup::Face)
	all=false
	unless faces[0]
		UI.beep
		sel.clear
		faces = ents.grep(Sketchup::Face)
		all=true
	end
	unless all ### remember faces for later
		unused_faces = ents.grep(Sketchup::Face) - faces
	end
	faces3=faces.find_all{|face| face.vertices.length==3 }
	faces = faces.dup - faces3
	###
	begin
		model.start_operation("TriangulateAllFaces", true)
	rescue
		model.start_operation("TriangulateAllFaces")
	end
	msg1="TriangulateAllFaces: "
	ct="1"
	msg2=" of #{faces.length}..."
	faces.each{|face|
		Sketchup.status_text=msg1+"Triangulating Non-Tri-Faces: "+ct+msg2
		self.triangulate(face)
		begin
			model.active_view.refresh
		rescue
			model.active_view.invalidate
		end
		ct.next!
	}
	### now make all tri-faces split evenly
	if all
		faces = ents.grep(Sketchup::Face)
	else ### it's a selection with new split tris etc
		faces = ents.grep(Sketchup::Face) - unused_faces
		sel.clear
		sel.add(faces)
	end
	###
	return nil unless faces[0]
	###
	@mode="Bisect-Angle" unless @mode
	results=inputbox(["Mode:               "], [@mode], ["Bisect-Angle|Centroid"], "TriangulateAllFaces")
	return nil unless results
	@mode=results[0]
	###
	msg1="TriangulateAllFaces: "
	ct="1"
	msg2=" of #{faces.length}..."
	faces.each{|face|
		Sketchup.status_text=msg1+"Triangulating Tri-Faces: "+ct+msg2
		self.triangulate_triangles(face)
		begin
			model.active_view.refresh
		rescue
			model.active_view.invalidate
		end
		ct.next!
	}
	sel.clear
	model.commit_operation
  end#def
  ###
  def self.triangulate_triangles(face)
	case @mode
		when "Bisect-Angle"
			ps=[]
			face.vertices.each{|v| ps << v.position}
			p0=ps[0]
			p1=ps[1]
			p2=ps[2]
			v1=p0.vector_to(p1)
			v2=p0.vector_to(p2)
			a12=v1.angle_between(v2) / 2.0
			nor=v1.cross(v2)
			tr=Geom::Transformation.rotation(p0, nor, a12)
			v=v1.transform(tr)
			lin12=[p0, v]
			###
			v1=p2.vector_to(p1)
			v0=p2.vector_to(p0)
			a01=v0.angle_between(v1) / 2.0
			nor=v0.cross(v1)
			tr=Geom::Transformation.rotation(p2, nor, a01)
			v=v0.transform(tr)
			lin01=[p2, v]
			pt=Geom.intersect_line_line(lin12, lin01)
			ents=face.parent.entities
			e0=ents.add_line(pt, p0)
			e1=ents.add_line(pt, p1)
			e2=ents.add_line(pt, p2)
			tr=Geom::Transformation.new()
			ents.intersect_with(true, tr, ents, tr, true, [e0, e1, e2])
		else ### Centroid
			ps=ORIGIN.clone
			vs=face.vertices
			vs.each{|v|ps += ORIGIN.vector_to(v.position)}
			cp=Geom::Point3d.new(ps.x/3.0, ps.y/3.0, ps.z/3.0)
			ents=face.parent.entities
			e0=ents.add_line(cp, vs[0].position)
			e1=ents.add_line(cp, vs[1].position)
			e2=ents.add_line(cp, vs[2].position)
			tr=Geom::Transformation.new()
			ents.intersect_with(true, tr, ents, tr, true, [e0, e1, e2])
	end
	###
  end
end#module
end#module
