-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
--------___________________________Voronoi 3D v0.1___________________________--------
--------                                                                     --------
-------------------------------------------------------------------------------------
-- Written by dimitris gourdoukis.                                                 --
-- object-e architecture 2009.                                                     --
-------------------------------------------------------------------------------------
-- http://object-e.blogspot.com/                                                   --
-- 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 08/01/09. Tested on 3DStudio Max 9.                          --
-- Use and modify at your own risk.                                                --
-------------------------------------------------------------------------------------
-- Description:                                                                    --
--	This script calculates the 3-dimentional voronoi diagram for a number of any   --
--  type of selected objects inside a user defined container. To use run the script--
--	then go under utilities/maxscript/voronoi 3d. First specify the container using--
--  the pick button, then select the objects to use as the voronoi centers (their  --
--  pivot points) and then hit "Build Voronoi". There is also another option that  --
--  builds spheres with their centers at the pivot points of the sellected objects --
--  and with radius half the distance from the closest neighbor point.             --
--  Note: The script does not sort the points that it uses in any way. That means  --
--        that it tests each point against every other point in the selection.     --
--        Therefore, a large number of input points might crash your computer!     --
-------------------------------------------------------------------------------------

utility voronoi "Voronoi 3D"
(
	fn containerFilter con = superClassOf con == GeometryClass
	
	group "About:"
	(
		label ca_label01 "Voronoi 3D v0.1" 
		label ca_label02 "dimitris gourdoukis, 2009"
		hyperlink hl_hp "object-e architecture" color:(color 0 0 255) address:"http://object-e.blogspot.com" align:#center
	)
	group "Container:"
	(
		pickbutton theContainer "Pick Container" width:140 height:25 filter: containerFilter tooltip:"select object to use for container"
	)
	
	group "Voronoi:"
	(
		button start_process "BUILD VORONOI" width:140 height:30 tooltip:"first select objects to use as centers"
	)
	
	group "Voronoi Spheres:"
	(
		button build_spheres "BUILD SPHERES" width:140 height:20 tooltip:"build spheres inside the cells"
	)
	
	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
			--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
	(
		if selection.count == 0 or selection.count == 1 then
		(
			messageBox "Select at least 2 objects" beep:false
		)
		else
		(
			i=0
			--start a progress indicator
			progressstart "building the Cells..."
		
			for obj in selection do
			(
				i = i+1
				--update the progress indicator
				progressupdate (i as float /selection.count *100)
				ranwire = random 15 245
				--create a copy of the container
				cont = copy $container
				cont.name = uniqueName "Cell_"
				cont.wirecolor = [ranwire, ranwire, ranwire]
				
				for poi in selection where poi != obj do
				(
					--calculate the position between 2 points
					newPos = (poi.pos + obj.pos)/2
					
					--calculate the vector between the 2 points
					dirVec = [poi.pos[1] - obj.pos[1], poi.pos[2] - obj.pos[2], poi.pos[3] - obj.pos[3]]
					
					--create a plane at the new position with the direction of the dirVec
					newPlane = plane pos: newPos widthSegs:1 lengthSegs:1 dir: dirVec
					
					--get the TM of the container
					contTM=cont.objecttransform
					
					--add a slice modifier at the container
					modSlice = sliceModifier slice_type:2
					addModifier cont modSlice
					
					--asign the rotation and the position of the plane to the slice plane
					--position of the slice plane is in local space - multiply with the inverse of the container's TM
					modSlice.slice_plane.rotation = newPlane.rotation
					modSlice.slice_plane.pos = newPlane.pos*(inverse contTM)
					
					--add a cap holes modifier
					addModifier cont (cap_holes())
					
					--collapse the modifier stack
					collapseStack cont
					
					--delete the plane
					delete newPlane
				)--end poi loop
			)--end obj loop
			
			--end the progress indicator
			progressend () 
			
			$container.isHidden = true
			$container.name = uniqueName "theContainer_"
		)--end else
		
	)--end on start_process
	
	on build_spheres pressed do
	(
		if selection.count == 0 or selection.count == 1 then
		(
			messageBox "Select at least 2 objects" beep:false
		)
		else
		(
			--collect the selected objects and add their pos in an array
			selCoo = #()
			for cent in selection do
			(
				append selCoo cent.pos
			)-- end cent loop
			
			size = selCoo.count
			
			for cent in selection do
			(
				dist = #()
				for i in 1 to size do
				(
					--calculate distance from all other objects (including self!!)
					dist[i] = distance cent.pos selCoo[i]			
				)--end i loop
				
				sort dist
				
				--calculate the radius r. dist[1] is always 0, therefore use dist[2]
				r = dist[2]/2
				Sphere radius:r smooth:on segs:32 chop:0 slice:off sliceFrom:0 sliceTo:0 mapcoords:on recenter:off pos:cent.pos
			
			)--end for cent in selection
		)--end else		
	)--end on build_spheres
)--end utility
