# Supports Organizer.rb
=begin rdoc
= Greeble 2
Copyright 2009, Chris Fullmer
All Rights Reserved

== Disclaimer
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.

== License
This software is distributed under the Smustard End User License Agreement
http://www.smustard.com/eula

== Information
Author:: Chris Fullmer
Organization:: www.ChrisFullmer.com and distributed on www.smustard.com
Name:: Greeble 2
Version:: 2.21
SU Version:: I developed this on SketchUp version 7, but older versions should work.
Date:: August 31, 2009
Description:: This script adds Greebles to all selected faces or to all loose geometry if nothing is selected
Usage:: Select the faces you would like to be Greebled.  Then run the script (Plugins>Greeble).  Put in min/max height for Greebles.  You can also add an offset to be used. A negative value is an inset face.  The Greeble offset or original face will change which face gets greebled.  I think that playing with that option will help explain it.  You can also specify a scale amount as a percent.  100 is no scale, 20 is smaller, 200 is twice as large as the original.
Requires:: Progressbar.rb from http://www.smustard.com/script/ProgressBar and offset.rb from http://www.smustard.com/script/Offset 
History::
0.01:: 2009-02-01
   * Original release. Plenty of bugs.  Some known, probably lots of unknown.
0.011:: 2009-02-01
   * Upgrade to the original script.  Now it supports the SketchUp 7 ruby speedboost.
0.02:: 2009-02-02
   * Added min/max options to the offset. (super buggy apparently)
   * Added some checks on the menu inputs to help make sure that useable values are entered.
   * Added rough metric support.
0.025:: 2009-02-03
   * Fixed a bunch of errors in previous version including the random # generator, offset distances work consistently.
   * Added a face normal check to make sure new faces created with offset match the original face direction.
0.03:: 2009-02-06
   * Added a validity check on the face about to be push/pulled to reduce breakage.
   * Ability to Set min/max offset distances is now much more stable.
   * Correctly uses the speed up capability of SU 7.
   * Works with SU 6 (and lower).
   * Uses (and requires) ProgressBar.rb to show its working and estimate how much time it will take.
0.04:: 2009-02-09
   * Added "Happy Greeble-ing" to the status bar
2.00:: 2009-02-11
   * First non beta release, and we'll just skip version 1 altogether!
   * Added the ability to scale the greebles
   * Propmts to group greeble "roofs" all together in one group.  Useful for some applications of the script.
2.10:: 2009-02-13
   * The first Smustard release!
   * Update rdoc compatiblity
   * New license info to conform to Smustard's EULA
2.11:: 2009-03-09
   * Organizer.rb support. If you place this script into an organizer directory, it will appear in the correct submenu.  If its just in the plugins folder, it will appear in "Chris Fullmer Tools" submenu of the Plugins Menu."
2.2:: 2009-08-31
   * Fixed a MAJOR bug that would cuase SketchUp to crash when greeble was used inside a group or component.  So yes, you can now safely use Greeble inside a group or component! 
   * Changed some code that should theoretically speed things up ever so slightly....frankly I don't think it helped.
   * Changed some code to make it run smarter, skip certain methods if they were not being executed.
   * Fixed some code that could cause problems when faces would overlap.
2.21:: 2009-08-31
   * Now user input settings are rembered between script uses.
=end

require "sketchup.rb"
require "offset.rb"
require "progressbar.rb"

module Clf_greeble_2

#class Clf_greeble_class

@@first_run = true
@@min_height = []
@@max_height = []
@@min_offset = []
@@max_offset = []
@@greeble_face = []
@@scale = []

	# This method accepts a face and a height to push the face.
	# It returns the Object Reference of the new face that is
	# parallel to the original face.
	def Clf_greeble_2.push_me(e_face, p_height)
		e_ents = []
		c_ents = []
		n_ents = []
		count = 0
		new_face = 0
		e_ents = e_face.all_connected.to_a
		e_face.pushpull( p_height, true)
		c_ents = e_face.all_connected.to_a
		n_ents = c_ents - e_ents
		n_ents.each do |ent|
			if ent.is_a? Sketchup::Face
				if ent.normal == e_face.normal
					new_face = ent
					break
				end
			end
		end
		new_face
	end
	
	# This method accepts a face and a float number.  It then performs unfirom scale
	# on the face using the faces centerpoint and the float as the percentage to scale.
	def Clf_greeble_2.scale_on_center( e_face, s_percent )
		cp = Geom::Point3d.new( e_face.bounds.center )
		scale_t = Geom::Transformation.scaling( cp, s_percent.to_f )
		#UI.messagebox "1"
		e_face.parent.entities.transform_entities( scale_t, e_face )
		#UI.messagebox "2"
	end
	
	# This is the main method that is called from the Plugin menu.  It executes most of the script
	# and does the bulk of the work.
	def Clf_greeble_2.greeble
		model = Sketchup.active_model
		clselection = model.selection
		retry_menu = 0
		
		if @@first_run
			values = ["1'", "5'", "-1'", "-2'", "0", "100"]
			@@first_run = false
		else
			values = [ @@min_height, @@max_height, @@min_offset,	@@max_offset, @@greeble_face, @@scale]
		end

		result = Sketchup.set_status_text( "Happy Greeble-ing! Chris", SB_PROMPT )
		group_greeble_top = 0
		 
		# This checks to see if the user has anything selected.  If the selection is empty, a warning is issued.
		if clselection.empty?
			UI.messagebox "You have nothing selected.  This will Greeble all faces that are not grouped."
			entities = model.active_entities.to_a
		else
			entities = model.selection.to_a
		end
		 
		# This is the first menu.  It is set to run while the variable retry_menu is set to 0.  So once the menu is complete,
		# the retry_menu is set to 1.  But if there was an incorrect value entered, then retry_menu gets set back to zero and the
		# menu is re-run.
		while retry_menu == 0
			prompts = ["Min Height", "Max Height", "Min Offset Distance (negative is inward)", "Max offset Distance (negative is inward)",
			"Greeble the offset face(0) or original face(1)?", "Scale the Greeble? (In percent)"]
			results = inputbox prompts, values, "Box Dimensions"
			
			@@min_height = results[0]
			@@max_height = results[1]
			@@min_offset = results[2]
			@@max_offset = results[3]
			@@greeble_face = results[4]
			@@scale = results[5]
			
			
			retry_menu = 1
			if @@min_height.to_l > @@max_height.to_l
				UI.messagebox "The Minimum height needs to be equal to or smaller than the maximum height."
				retry_menu = 0
				@@min_height = @@max_height
			end
			if @@greeble_face.to_i == 0 or @@greeble_face.to_i == 1
			else
				UI.messagebox "Greeble the offset(0) or original(1) face can only be set to 0 or 1."
				retry_menu = 0
				@@greeble_face = 0
			end
			if	(@@min_offset.to_l >= 0 and @@max_offset.to_l >= 0) or (@@min_offset.to_l <= 0 and @@max_offset.to_l <= 0)
				if @@max_offset.to_l.abs - @@min_offset.to_l.abs < 0
					retry_menu = 0
					@@min_offset = @@max_offset
					UI.messagebox "Max offset must be a larger number than min offset"
				end
			else
				UI.messagebox "Offset Min/Max values need to be either both positive or both negative"
				retry_menu = 0
			end	
			if @@scale.to_i <= 0
				UI.messagebox "The scale needs to be a positive number and must be larger than 0."
				retry_menu = 0
				@@scale = 100
			end
			
			values = [@@min_height, @@max_height, @@min_offset, @@max_offset, @@greeble_face, @@scale]
		end
		
		# Asks the user if they want to group the top faces separately.  Useful in some applications of the script.
		group_greeble_top = UI.messagebox( "Would you like to group separately all the topmost Greeble faces?", MB_YESNOCANCEL, "Menu" )
		if group_greeble_top == 2
			return nil
		end
		
		clent = entities[0]
		clcount = 0
		clface = []
		greebletops = []
		
		a = Sketchup.version
		b = a[0,1].to_i
		if b > 6
			model.start_operation("Greeble", true)
		else
			model.start_operation("Greeble")
		end
		
		# This leaves me with an array of just the faces.
		pb = ProgressBar.new(entities.length,"Finding Faces from #{entities.length} total entities")
		entities.each do |e|
			pb.update(clcount)
			clface << e if e.is_a? Sketchup::Face
			clcount = clcount + 1
		end

		clrandheightdif = @@max_height.to_l - @@min_height.to_l
		clrandoffsetdif = @@max_offset.to_l - @@min_offset.to_l
		cloffset = clrandoffsetdif + @@min_offset.to_i + @@max_offset.to_i
		clrandoffsetneg = 1        # Arbitrarily sets the value to 1 which is Positive.
		if @@max_offset.to_l < 0     # If the offset values are negative, then it changes the value to 0.
			clrandoffsetneg = 0
		end
		pb2 = ProgressBar.new(clface.length,"Making #{clface.length} Greebles")
		clcount = 0
		clface.each do |e|
			pb2.update(clcount)
			if e.valid?
				if @@min_offset.to_i == 0 and @@max_offset.to_i == 0
					if clrandheightdif == 0
						new_face = push_me( e, @@min_height.to_l )
					else
						clpp = rand(clrandheightdif) + @@min_height.to_l + 1
						new_face = push_me( e, clpp )
					end
				else
					clpp = rand(clrandheightdif) + @@min_height.to_l + 1
					if clrandoffsetdif == 0
						cl_newface = e.offset @@min_offset.to_l
					else
						if clrandoffsetneg == 1 #decides if the randomoffset difference is negative. 0 is negative.
							clrandoffset = rand(clrandoffsetdif) + @@min_offset.to_l + 1
							cl_newface = e.offset clrandoffset
						else
							clrandoffset = -(rand(clrandoffsetdif.abs) + @@min_offset.to_l.abs + 1)
							cl_newface = e.offset clrandoffset
						end 
					end
					
					# Tests the direction of the new face to the original face.  If they don't face the same direction,
					# the new face gets reversed to match the original face.
					orig_vector = e.normal
					new_vector = cl_newface.normal
					if orig_vector != new_vector
						cl_newface.reverse!
					end
					if clrandheightdif == 0
						if @@greeble_face.to_i == 0
							new_face = push_me( cl_newface, @@min_height.to_l )
						else
							new_face = push_me( e, @@min_height.to_l )
						end
					else
						if @@greeble_face.to_i == 0
							new_face = push_me( cl_newface, clpp )
						else
							new_face = push_me( e, clpp )
						end
					end
				end
				greebletops.push new_face
				scale_percent = (@@scale.to_f / 100.0)
				Clf_greeble_2.scale_on_center(new_face, scale_percent) unless @@scale.to_i == 100
			end
			clcount = clcount + 1
		end
		if group_greeble_top == 6 # Groups greeble tops if the users said "yes" earlier.
			entities = model.active_entities
			group = entities.add_group( greebletops )
		end
	model.commit_operation
	end
end



if( $submenu )
	organizer = $submenu 
	if !file_loaded?(__FILE__) then
		organizer.add_item("Greeble 2") { Clf_greeble_2.greeble }
	end
else
	if !file_loaded?('clf_menu_loader') then
		$clf_tools_menu = UI.menu("Plugins").add_submenu("Chris Fullmer Tools")
	end
	if !file_loaded?(__FILE__) then
		$clf_tools_menu.add_item("Greeble 2") { Clf_greeble_2.greeble }
	end
end

file_loaded('clf_menu_loader')
file_loaded(__FILE__)