-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
--------________________________Sphere Packing v1.0__________________________--------
--------                                                                     --------
-------------------------------------------------------------------------------------
-- Written by dimitris gourdoukis.                                                 --
-- object-e architecture 2009.                                                     --
-------------------------------------------------------------------------------------
-- http://object-e.net/                                                            --
-- object.e.architecture@gmail.com                                                 --
-------------------------------------------------------------------------------------
-- released under the creative commons license "share alike unported 3.0"          --
-- http://creativecommons.org/licenses/by-sa/3.0/                                  --
-------------------------------------------------------------------------------------
-- v1.0 Last Modified 10/29/09. Tested on 3DStudio Max 9.                          --
-- Use and modify at your own risk.                                                --
-------------------------------------------------------------------------------------
-- Description:                                                                    --
--	This script packs a number of spheres inside a selected volume with no         --
--  overlapping. Each new sphere is tangent to an existing one.                    --
-- Note 1: the centers of the spheres are inside the selected volume, so the       --
--  spheres themselves might intersect with the volume.                            --
-- Note 2: There are NO safety valves whatsoever for this script, so use with      --
--  caution! First make sure that the values that you use are making sense!!       --
-------------------------------------------------------------------------------------

global rltSpherePack
try destroydialog rltSpherePack catch()


rollout rltSpherePack "Sphere Packing v1.0" width:259 height:300
(
	fn containerFilter con = superClassOf con == GeometryClass
	group "About:"
	(
		label ca_label01 "Sphere Packing v1.0" 
		label ca_label02 "dimitris gourdoukis, 2009"
		hyperlink hl_hp "object-e architecture" color:(color 0 0 255) address:"http://object-e.net" align:#center
	)
	
	group "Container:"
	(
		pickbutton theContainer "Pick Container" width:140 height:25 filter: containerFilter tooltip:"bounding box of picked object"
	)
	
	group "Settings:"
	(
		spinner nSpheres "# of Spheres:" range:[4,1000,20] type:#integer fieldwidth:40 align:#right
		spinner minRad "min Radius:" range:[1,100,1] type:#float fieldwidth:40 align:#right
		spinner maxRad "max Radius:" range:[2,200,6] type:#float fieldwidth:40 align:#right 
	)
	
	--generate the main button
	button start_process "BUILD IT!" width:140 height:30
	
	on theContainer picked con do
	(
		--see if the user did not cancel the picking...
		if con != undefined do
		(
			--if he did not, make the box's wireframe red:
			con.wirecolor = red
			BBcor = #(con.min,con.max)
			con.isHidden = true
			clearSelection()
			--and display the name of the object on the button:
			con.name = "container"
			theContainer.text = con.name
		)


	)	-- end on theContainer picked
	
	on start_process pressed do
	(
		cont = $container
		BBcor = #(cont.min,cont.max)
		--define the bounding box
		a = BBcor[1] -- define point A
		b = BBcor[2] -- define point B
		--find the diagonal
		c = distance a b
		--create an array to contain the spheres
		sphereSet = #()
		--create the first sphere at pos w with radius ra and add it to the array
		w = random a b
		ra = random minRad.value maxRad.value
		sp = sphere pos: w radius: ra wirecolor: [200, 200, 200]
		sphereSet[1] = sp
		for i in 1 to (nSpheres.value - 1) do
		(
			found = true
			while found do
			(
				--random radius
				r = random minRad.value maxRad.value
				--a random number to select randomly from the existing spheres
				ind = random 1 i
				--the sum of the random radius and the radius of the randomly selected sphere
				sumRad = r + sphereSet[ind].radius
				--create a geosphere to use for the placement of the sphere
				dumSph = GeoSphere pos: sphereSet[ind].pos radius: sumRad baseType: 1
				addModifier dumSph (Edit_Mesh ())
				--select randomly one of the vertices of the geosphere and get its position
				vn = random 1 60
				cpoint = dumSph.verts[vn].pos
				--testdist1 = distance cpoint a
				--testdist2 = distance cpoint b
				distances = #()
				if (cpoint[1] > a[1] and cpoint[1] < b[1] and cpoint[2] > a[2] and cpoint[2] < b[2] and cpoint[3] > a[3] and cpoint[3] < b[3]) then
				(
					for j in 1 to i do
					(
						dum = sphereSet[j]
						dist = distance cpoint dum.pos
						testV = dist - (r + dum.radius)
						if (testV > 0 or testV == 0)do (append distances dist)
						
					)--end j loop
			
					if distances.count == i then
					(
						sph = sphere pos: cpoint radius: r wirecolor: [200, 200, 200]
						found = false
						append sphereSet sph
						delete dumSph
					)
					else 
					(
						delete dumSph
						found = true
					)
				)
				else
				(
					delete dumSph
					found = true
				)
				
			)--end while found do
		)--end i loop
	)--end on start_process do
)--end rollout

createDialog rltSpherePack
