#
# Name:		PocketTool.rb
# Desctiption:	Create a pocket face and zigzag edges
# Author:	Katsuhiko Toba ( http://www.eprcp.com/ )
# Usage:	1. Install into the plugins directory.
#		2. Select "Pocket" from the Plugins menu.
#		3. Click the face to pocket
#		4. Select "CenterLine Tool" from menu or toolbar
#		5. Click the zigzag edge first, the face edge second.
#		NOTE: Do not use Centerline from context menu.
#                     It breaks the zigzag edge.
# Limitations:	Simple convex face only
#
# ** Modified by kyyu 05-29-2010 - rewrote "get_offset_points" method because of a bug          **
#   where the pocket lines were out of the pocket boundaries because of mix direction edges     **
#   -Looks like it works, but not rigorously check so USE AT YOUR OWN RISK!                     **
#
# ** Modified by swarfer 2013-05-20 - press shift to only get zigzag, press ctrl to only get outline
#    This is a step on the way toward integrating it into Sketchucam, and properly handling complex faces
# $Id: PocketTool.rb 1.1 2013/05/20 11:02:14 david Exp $

require 'sketchup.rb'

class PocketTool
	def initialize
		@bit_diameter = 6.0.mm
		puts "bit diameter #{@bit_diameter.to_mm}mm"
		@ip = nil
		@active_face = nil
# set this to change zigzag setover, less for hard material, more for soft
		@setover_percent = 75.0
		puts "setover percent #{@setover_percent}%"
		if (@setover_percent < 100)
		    @setOver = @setover_percent / 100
		else
		    @setOver = 0.5
		end
		puts "setOver = #{@setOver}"
		@keyflag = 0
#		puts "keyflag = #{@keyflag}"
	end

	def activate
		@ip = Sketchup::InputPoint.new
#		Sketchup::set_status_text "PocketTool Activated", SB_VCB_LABEL
		msg = "Pockettool: [shift] for only zigzag [ctrl] for only boundary"
		Sketchup::set_status_text msg, SB_PROMPT
		self.reset(nil)
	end

	def reset(view)
		if (view)
			view.tooltip = nil
			view.invalidate
		end
	end

	def draw(view)
		if (@active_face)
			self.draw_geometry(view)
		end
	end

#SWARFER, want to detect the shift key down, which will prevent drawing an offset line
# ie when shift pressed, only do the zigzag inside the current face
	def onKeyDown(key, repeat, flags, view)
	   if (key == VK_SHIFT)
	      @keyflag = 1
	   end
	   if (key == VK_CONTROL)
              @keyflag = 2
           end
	end
	def onKeyUp(key, repeat, flags, view)
	   if key = VK_SHIFT || key = VK_CONTROL
	      @keyflag = 0
	   end
	end

	def onMouseMove(flags, x, y, view)
		@ip.pick view, x, y
		@active_face = activeFaceFromInputPoint(@ip)
		if (@active_face)
			view.tooltip = @ip.tooltip
		end
		view.invalidate if (@ip.display?)
	end

	def onLButtonDown(flags, x, y, view)
		@ip.pick view, x, y
		@active_face = activeFaceFromInputPoint(@ip)
		if (@active_face)
			self.create_geometry(@active_face, view)
		end
		self.reset(view)
		view.lock_inference
	end

	def activeFaceFromInputPoint(inputPoint)
		face = inputPoint.face

		# check simple face (outer_loop only)
		if (face)
			if (face.loops.length != 1)
				face = nil
			end
		end
		return face
	end

	def draw_geometry(view)

		view.drawing_color = "Green"
		#view.line_width = 3.0
		if (@keyflag == 1) || (@keyflag == 0)
		   zigzag_points = get_zigzag_points(@active_face.outer_loop)
		else
		   zigzag_points = nil
		end

		if (@keyflag == 2) || (@keyflag == 0)
		   contour_points = get_contour_points(@active_face.outer_loop)
		else
	           contour_points = nil
		end
		if (zigzag_points != nil)
		   if (zigzag_points.length >= 2)
			view.draw GL_LINE_STRIP, zigzag_points
   		   end
		end

		if (contour_points != nil)
	 	   if (contour_points.length >= 3)
			view.draw GL_LINE_LOOP, contour_points
   		   end
	 	end
	end

	def create_geometry(face, view)
#		puts "create geometry"
		model = view.model
		model.start_operation "Creating Pocket"

		if @keyflag == 1 || @keyflag == 0
		   zigzag_points = get_zigzag_points(@active_face.outer_loop)
		else
		   zigzag_points = nil
		end

		if (@keyflag == 2) || (@keyflag == 0)
		   contour_points = get_contour_points(@active_face.outer_loop)
		else
		   contour_points = nil
		end

		if zigzag_points != nil
		   if (zigzag_points.length >= 2)
			model.entities.add_edges(zigzag_points)
		   end
		end
		if (contour_points != nil)
		   if (contour_points.length >= 3)
   			# reverse points for counter clockwize loop
			   model.entities.add_face(contour_points.reverse!)
   		   end
		end

		model.commit_operation
	end

	#----------------------------------------------------------------------
	# generic offset points routine
	#----------------------------------------------------------------------

	def get_intersect_points(lines)
#		puts "Get intersect points"
		pts = []
		for i in 0..lines.length-1 do
			line1 = lines[i-1]	# array[-1] is equal to array[array.length-2]
			line2 = lines[i]
			pt = Geom::intersect_line_line(line1, line2)
			if (pt)
				pts << pt
			end
		end
		return pts
	end

	def get_offset_points(loop, offset)
#		puts "get offset points"
		normal_vector = Geom::Vector3d.new(0,0,-1)
        lines = []
        r = []
        notr = []
        for edge in loop.edges
            if edge.reversed_in? @active_face then r.push edge
            else notr.push edge
            end
        end

        for edge in loop.edges
            pt1 = edge.start.position
			pt2 = edge.end.position

			line_vector = edge.line[1]
			line_vector.normalize!
			move_vector = line_vector * normal_vector
			move_vector.length = offset

            if r.length != 0 and notr.length != 0
                if edge.reversed_in? @active_face
                    lines <<  [pt1.offset(move_vector.reverse), pt2.offset(move_vector.reverse)]
                else
                    lines <<  [pt1.offset(move_vector), pt2.offset(move_vector)]
                end
            elsif r.length == 0
			lines <<  [pt1.offset(move_vector), pt2.offset(move_vector)]
            elsif notr.length == 0
            lines <<  [pt1.offset(move_vector.reverse), pt2.offset(move_vector.reverse)]
            end
		end

		points = get_intersect_points(lines)
        return points

		# loop_length = 0.0	# length of original loop
		# normal_length = 0.0
		# reverse_length = 0.0

		# normal_lines = []
		# reverse_lines = []

		# for edge in loop.edges
			# pt1 = edge.start.position
			# pt2 = edge.end.position
			# loop_length += pt1.distance pt2

			# line_vector = edge.line[1]
			# line_vector.normalize!
			# move_vector = line_vector * normal_vector
			# move_vector.length = offset

			# normal_lines <<  [pt1.offset(move_vector), pt2.offset(move_vector)]
            # reverse_lines <<  [pt1.offset(move_vector.reverse), pt2.offset(move_vector.reverse)]
		# end

		# normal_points = get_intersect_points(normal_lines)
		# for i in 0..normal_points.length-1 do
			# pt1 = normal_points[i-1]
			# pt2 = normal_points[i]
			# normal_length += pt1.distance pt2
		# end

		# reverse_points = get_intersect_points(reverse_lines)
		# for i in 0..reverse_points.length-1 do
			# pt1 = reverse_points[i-1]
			# pt2 = reverse_points[i]
			# reverse_length += pt1.distance pt2
		# end

		# puts "loop_length="+loop_length.to_s
		# puts "normal_length="+normal_length.to_s
		# puts "reverse_length="+reverse_length.to_s

		# if (normal_length > loop_length)
			# if (offset > 0.0)
				# return normal_points
			# else
				# return reverse_points
			# end
		# else
			# if (offset > 0.0)
				# return reverse_points
			# else
				# return normal_points
			# end
		# end
	end

	#----------------------------------------------------------------------
	# contour
	#----------------------------------------------------------------------

	def get_contour_points(loop)
#		puts "get contour points"
		return get_offset_points(loop, -(@bit_diameter * 0.5))
	end

	#----------------------------------------------------------------------
	# zigzag
	#----------------------------------------------------------------------

	def get_hatch_points(points, y)
		plane = [Geom::Point3d.new(0, y, 0), Geom::Vector3d.new(0,1,0)]
		pts = []
		for i in 0..points.length-1 do
			y1 = points[i-1].y
			y2 = points[i].y
			next if (y1 == y2)
			next if ((y1 > y2) && ((y > y1) || (y < y2)))
			next if ((y1 < y2) && ((y < y1) || (y > y2)))

			line = [points[i-1], points[i]]
			pt = Geom::intersect_line_plane(line, plane)
			if (pt)
				pts << pt
			end
		end
		pts.uniq!
		return pts.sort{|a,b| a.x <=> b.x}
	end

	def get_zigzag_points(loop)
#		puts "get zigzag points"
		dir = 1
		zigzag_points = []
		if @keyflag == 1
		   offset = @bit_diameter * 0.1
		else
		   offset = @bit_diameter * 0.6
		end

		offset_points = get_offset_points(loop, -(offset))

		bb = loop.face.bounds
		if @keyflag == 0
		   y = bb.min.y + offset
		else
		   y = bb.min.y + offset
		end


		while (y < bb.max.y) do
			pts = get_hatch_points(offset_points, y)
			if (pts.length >= 2)
				if (dir == 1)
					zigzag_points << pts[0]
					zigzag_points << pts[1]
					dir = -1
				else
					zigzag_points << pts[1]
					zigzag_points << pts[0]
					dir = 1
				end
			end
			y = y + @bit_diameter * @setOver
			if (@setOver <= 0) # prevent infinite loop
			   break;
				#code
			end

		end

		return zigzag_points
	end

end

#------------------------------------------------------------------------
def pockettool
	Sketchup.active_model.select_tool PocketTool.new
end

if( not file_loaded?("PocketTool.rb") )
	main_menu = UI.menu("Plugins")
	main_menu.add_item("Pocket") {(pockettool)}
end
file_loaded("PocketTool.rb")

#------------------------------------------------------------------------

# $Log: PocketTool.rb $
# Revision 1.1  2013/05/20 11:02:14  david
# Initial revision
#
