/*
Dimension

Version: 	1.0.0-6
Created: 	26.06.2009
Edited:	27.06.2009
Author: 	Andreas Meißner

Dimensionize the vertselection of your current object.
This script is based on the idea of the Lightwave-script "Dimension" by Gianni Soldati.
... hope it helps :D
*/

(
	try destroyDialog rlDimension catch()
	
	global rlDimension = rollout rlDimension "Dimension" (
		-- variables
			local iSpinW = 80
			local iButH = 16
			local iButW = 35
			local iMaxVal = 999999
			local iDefVal = 1
			local fBboxThresh = 0.01
			local fPrevX = iDefVal
			local fPrevY = iDefVal
			local fPrevZ = iDefVal
			local iSideMult = 1
		-- controls
			local p2X = [22,3]
			label labDim "Dimension"
								pos:p2X
			label labSideAlign "Side aligned"
								pos:(p2X+[127,0])
			local p2X = [15,20]
			-- x
				spinner spX "X " 
									pos:p2X
									width:iSpinW
									range:[0, iMaxVal, iDefVal]
				label labX "     "
									pos:(p2X+[iSpinW+5,0])
				checkbutton cbXNeg "-"
									pos:(p2X+[iSpinW+30,0])
									width:iButW height:iButH
				checkbutton cbXCenter "center"
									pos:(p2X+[iSpinW+iButW+30,0])
									width:iButW height:iButH
									checked:true
				checkbutton cbXPos "+"
									pos:(p2X+[iSpinW+2*iButW+30,0])
									width:iButW height:iButH
			-- y
				spinner spY "Y "
									pos:(p2X+=[0,20])
									width:iSpinW
									range:[0, iMaxVal, iDefVal]
				label labY "     "
									pos:(p2X+[iSpinW+5,0])
				checkbutton cbYNeg "-"
									pos:(p2X+[iSpinW+30,0])
									width:iButW height:iButH
				checkbutton cbYCenter "center"
									pos:(p2X+[iSpinW+iButW+30,0])
									width:iButW height:iButH
									checked:true
				checkbutton cbYPos "+"
									pos:(p2X+[iSpinW+2*iButW+30,0])
									width:iButW height:iButH
			-- z
				spinner spZ "Z "
									pos:(p2X+=[0,20])
									width:iSpinW
									range:[0, iMaxVal, iDefVal]
				label labZ "     "
									pos:(p2X+[iSpinW+5,0])
				checkbutton cbZNeg "-"
									pos:(p2X+[iSpinW+30,0])
									width:iButW height:iButH
				checkbutton cbZCenter "center"
									pos:(p2X+[iSpinW+iButW+30,0])
									width:iButW height:iButH
				checkbutton cbZPos "+"
									pos:(p2X+[iSpinW+2*iButW+30,0])
									width:iButW height:iButH
									checked:true
			checkbox chkMaintainProps "Maintain proportions"
								pos:(p2X+=[0,25])
			checkbox chkScaleAlsoUnsel "Scale also unselected"
								pos:(p2X+=[0,15])
			checkbox chkSideModSwitch "Use \"side aligned\" as \"side modified\""
								pos:(p2X+=[0,15])
			local iButH = 20
			button butApply "Apply"
								pos:(p2X+=[0,25])
								width:70 height:iButH
			button butOk "Ok"
								pos:(p2X+[70,0])
								width:70 height:iButH
			button butRefresh "Refresh"
								pos:(p2X+[140,0])
								width:70 height:iButH
		-- functions
			fn fnUpdate = (
				if selection.count != 0 and selection[1].mesh.selectedVerts.count != 0 do (
					-- fill values
						local Obj = selection[1]
						local fMinX, fMinY, fMinZ = undefined
						local fMaxX, fMaxY, fMaxZ = undefined
					if chkScaleAlsoUnsel.checked then (
						fMinX = Obj.min.x
						fMinY = Obj.min.y
						fMinZ = Obj.min.z
						fMaxX = Obj.max.x
						fMaxY = Obj.max.y
						fMaxZ = Obj.max.z
					) else (
						for Vert in Obj.mesh.selectedVerts do (
							if fMinX == undefined or Vert.pos.x < fMinX do fMinX = Vert.pos.x
							if fMinY == undefined or Vert.pos.y < fMinY do fMinY = Vert.pos.y
							if fMinZ == undefined or Vert.pos.z < fMinZ do fMinZ = Vert.pos.z
							if fMaxX == undefined or Vert.pos.x > fMaxX do fMaxX = Vert.pos.x
							if fMaxY == undefined or Vert.pos.y > fMaxY do fMaxY = Vert.pos.y
							if fMaxZ == undefined or Vert.pos.z > fMaxZ do fMaxZ = Vert.pos.z
						)
					)
					fPrevX = spX.value = abs (fMaxX - fMinX)
					fPrevY = spY.value = abs (fMaxY - fMinY)
					fPrevZ = spZ.value = abs (fMaxZ - fMinZ)
				)
			)
			fn fnDimension = (
				if selection.count != 0 do (
				-- get object
					local Obj = selection[1]
				-- backup mode
					local nCommPanTaskModeBak = getCommandPanelTaskMode()
					local iSubObjLevBak = undefined
					if nCommPanTaskModeBak == #modify do (
						iSubObjLevBak = subObjectLevel
						setCommandPanelTaskMode #create
					)
				-- change vertselection if "scale also unselected" checked
					local aVertSelBak = undefined
					if chkScaleAlsoUnsel.checked do (
						aVertSelBak = Obj.selectedVerts as bitarray
						Obj.selectedVerts = #{1..Obj.numVerts}
					)
				-- build dummy mesh based on positions of selected verts, backup vertindices of object
					local aSelVertPos = #()
					local aSelVertInd = #()
					for Vert in Obj.mesh.selectedVerts do (
						append aSelVertPos (Vert.pos + Obj.pivot)
						append aSelVertInd Vert.index
					)
					local Dum = mesh vertices:aSelVertPos faces:#()
					centerPivot Dum
				-- get source size
					local fSrcX = abs (Dum.max.x - Dum.min.x)
					local fSrcY = abs (Dum.max.y - Dum.min.y)
					local fSrcZ = abs (Dum.max.z - Dum.min.z)
				-- get decoded target size
					local fUnitConv = units.decodeValue "1"
					local fTargX = spX.value * fUnitConv
					local fTargY = spY.value * fUnitConv
					local fTargZ = spZ.value * fUnitConv
				-- get scale multiplier
					local fMultX = if fSrcX < fBboxThresh then 1 else fTargX / fSrcX
					local fMultY = if fSrcY < fBboxThresh then 1 else fTargY / fSrcY
					local fMultZ = if fSrcZ < fBboxThresh then 1 else fTargZ / fSrcZ
				-- get side align
					local nAlignX = if cbXNeg.checked then #neg else if cbXCenter.checked then #center else #pos
					local nAlignY = if cbYNeg.checked then #neg else if cbYCenter.checked then #center else #pos
					local nAlignZ = if cbZNeg.checked then #neg else if cbZCenter.checked then #center else #pos
				-- get position offset
					local fOffsX = 
						case nAlignX of (
							#neg: (fTargX-fSrcX)/2*iSideMult
							#pos: -(fTargX-fSrcX)/2*iSideMult
							#center: 0
						)
					local fOffsY = 
						case nAlignY of (
							#neg: (fTargY-fSrcY)/2*iSideMult
							#pos: -(fTargY-fSrcY)/2*iSideMult
							#center: 0
						)
					local fOffsZ = 
						case nAlignZ of (
							#neg: (fTargZ-fSrcZ)/2*iSideMult
							#pos: -(fTargZ-fSrcZ)/2*iSideMult
							#center: 0
						)
				-- apply dimension to dummy
					Dum.scale = [fMultX, fMultY, fMultZ]
					Dum.pos += [fOffsX,fOffsY,fOffsZ]
					resetXform Dum
					convertToMesh Dum
				-- sync positions of verts between dummy and selected verts of object
					undo label:"Dimension" true ( 
						for iVert=1 to Obj.mesh.selectedVerts.count do (
							local p3VertPos = Dum.mesh.verts[iVert].pos + Dum.pivot
							local iVertInd = aSelVertInd[iVert]
							case (classof Obj) of (
								Editable_mesh: meshop.setVert Obj iVertInd p3VertPos
								Editable_Poly: polyop.setVert Obj iVertInd p3VertPos
								
							)
						)
						update Obj
					)
				-- cleanup
					delete Dum
					if nCommPanTaskModeBak == #modify do (
						subObjectLevel = iSubObjLevBak
						setCommandPanelTaskMode nCommPanTaskModeBak
					)
					-- change vertselection if "scale also unselected" checked
						if chkScaleAlsoUnsel.checked do Obj.selectedVerts = aVertSelBak
				)
			)
		-- events
			-- main
				on rlDimension open do (
					-- set unit labels
						local sUnit = 
							case units.DisplayType of (
								#metric: (
									case units.MetricType of (
										#millimeters: "mm"
										#meters: "m"
										#centimeters: "cm"
										#kilometers: "km"
									)
								)
								#generic: ""
							)
						labX.text = labY.text = labZ.text = sUnit	
					-- update dimension
						fnUpdate()
					-- add callback for update on objectselection change
						callbacks.addScript #selectionSetChanged "rlDimension.fnUpdate();" id:#Dimension
					-- set side switch
						chkSideModSwitch.checked = if iSideMult == -1 then true else false
				)
				on rlDimension close do (
					callbacks.removeScripts id:#Dimension
				)
			-- values
				on spX changed fNewX do (
					if chkMaintainProps.checked do (
						spY.value *= fNewX/fPrevX
						spZ.value *= fNewX/fPrevX
					)
					fPrevX = fNewX
				)
				on spY changed fNewY do (
					if chkMaintainProps.checked do (
						spX.value *= fNewY/fPrevY
						spZ.value *= fNewY/fPrevY
					)
					fPrevY = fNewY
				)
				on spZ changed fNewZ do (
					if chkMaintainProps.checked do (
						spX.value *= fNewZ/fPrevZ
						spY.value *= fNewZ/fPrevZ
					)
					fPrevZ = fNewZ
				)
			-- side aligns
				on cbXNeg changed bChk do ( cbXNeg.checked = true; cbXCenter.checked = cbXPos.checked = false )
				on cbXCenter changed bChk do ( cbXCenter.checked = true; cbXNeg.checked = cbXPos.checked = false )
				on cbXPos changed bChk do ( cbXPos.checked = true; cbXCenter.checked = cbXNeg.checked = false )
				on cbYNeg changed bChk do ( cbYNeg.checked = true; cbYCenter.checked = cbYPos.checked = false )
				on cbYCenter changed bChk do ( cbYCenter.checked = true; cbYNeg.checked = cbYPos.checked = false )
				on cbYPos changed bChk do ( cbYPos.checked = true; cbYCenter.checked = cbYNeg.checked = false )
				on cbZNeg changed bChk do ( cbZNeg.checked = true; cbZCenter.checked = cbZPos.checked = false )
				on cbZCenter changed bChk do ( cbZCenter.checked = true; cbZNeg.checked = cbZPos.checked = false )
				on cbZPos changed bChk do ( cbZPos.checked = true; cbZCenter.checked = cbZNeg.checked = false )
			-- additional settings
				on chkSideModSwitch changed bChk do iSideMult = if bChk then -1 else 1
			-- main
				on butApply pressed do fnDimension()
				on butOk pressed do (
					fnDimension()
					destroyDialog rlDimension
				)
				on butRefresh pressed do fnUpdate()
	)
	
	createDialog rlDimension 240 163
)