-- **************************************************
-- Provide Moho with the name of this script object
-- **************************************************

ScriptName = "MR_TrackBone"

-- **************************************************
-- General information about this script
-- **************************************************

MR_TrackBone = {}

function MR_TrackBone:Name()
	return self:Localize('UILabel')
end

function MR_TrackBone:Version()
	return '1.0'
end

function MR_TrackBone:UILabel()
	return self:Localize('UILabel')
end

function MR_TrackBone:Creator()
	return 'Eugene Babich'
end

function MR_TrackBone:Description()
	return self:Localize('Description')
end


-- **************************************************
-- Is Relevant / Is Enabled
-- **************************************************

function MR_TrackBone:IsRelevant(moho)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return false
	end
	return true
end

function MR_TrackBone:IsEnabled(moho)
	local skel = moho:Skeleton()
	if (moho:CountBones() < 1) then
		return false
	end
	return true
end

-- **************************************************
-- Recurring Values
-- **************************************************

MR_TrackBone.frameRange = true
MR_TrackBone.from = 1
MR_TrackBone.to = 50
MR_TrackBone.interval = 1
MR_TrackBone.copiedInterval = 1
MR_TrackBone.pasteFromCurFrame = false
MR_TrackBone.trackAngle = false
MR_TrackBone.frames = {}
MR_TrackBone.globalPos = {}
MR_TrackBone.globalPosSecond = {}
MR_TrackBone.isReady = false
MR_TrackBone.maintainAngleOffset = true
MR_TrackBone.maintainPositionOffset = true
MR_TrackBone.recolorKeyframes = false
MR_TrackBone.colorTag = 0


-- **************************************************
-- Prefs
-- **************************************************

function MR_TrackBone:LoadPrefs(prefs)
	self.frameRange = prefs:GetBool("MR_TrackBone.frameRange", true)
	self.from = prefs:GetInt("MR_TrackBone.from", true)
	self.to = prefs:GetInt("MR_TrackBone.to", true)
	self.interval = prefs:GetInt("MR_TrackBone.interval", true)
	self.pasteFromCurFrame = prefs:GetBool("MR_TrackBone.pasteFromCurFrame", true)
	self.trackAngle = prefs:GetBool("MR_TrackBone.trackAngle", true)
	self.maintainAngleOffset = prefs:GetBool("MR_TrackBone.maintainAngleOffset", true)
	self.maintainPositionOffset = prefs:GetBool("MR_TrackBone.maintainPositionOffset", true)
	self.recolorKeyframes = prefs:GetBool("MR_TrackBone.recolorKeyframes", true)
	self.colorTag = prefs:GetInt("MR_TrackBone.colorTag", true)
end

function MR_TrackBone:SavePrefs(prefs)
	prefs:SetBool("MR_TrackBone.frameRange", self.frameRange)
	prefs:SetInt("MR_TrackBone.from", self.from)
	prefs:SetInt("MR_TrackBone.to", self.to)
	prefs:SetInt("MR_TrackBone.interval", self.interval)
	prefs:SetBool("MR_TrackBone.pasteFromCurFrame", self.pasteFromCurFrame)
	prefs:SetBool("MR_TrackBone.trackAngle", self.trackAngle)
	prefs:SetBool("MR_TrackBone.maintainAngleOffset", self.maintainAngleOffset)
	prefs:SetBool("MR_TrackBone.maintainPositionOffset", self.maintainPositionOffset)
	prefs:SetBool("MR_TrackBone.recolorKeyframes", self.recolorKeyframes)
	prefs:SetInt("MR_TrackBone.colorTag", self.colorTag)
end

function MR_TrackBone:ResetPrefs()
	self.frameRange = true
	self.from = 1
	self.to = 50
	self.interval = 1
	self.pasteFromCurFrame = false
	self.intervalMenu:SetChecked(self.INTERVAL_2, false)
	self.intervalMenu:SetChecked(self.INTERVAL_3, false)
	self.intervalMenu:SetChecked(self.INTERVAL_4, false)
	self.intervalPopup:Redraw()
	self.isReady = false
	self.trackAngle = false
	self.maintainPositionOffset = false
	self.maintainAngleOffset = true
	self.recolorKeyframes = false
	self.colorTag = 1
end

-- **************************************************
-- Keyboard/Mouse Control
-- **************************************************

function MR_TrackBone:OnMouseDown(moho, mouseEvent)
	
end

-- **************************************************
-- Tool Panel Layout
-- **************************************************

MR_TrackBone.FRAME_RANGE = MOHO.MSG_BASE
MR_TrackBone.FROM = MOHO.MSG_BASE + 1
MR_TrackBone.TO = MOHO.MSG_BASE + 2
MR_TrackBone.INTERVAL = MOHO.MSG_BASE + 3
MR_TrackBone.TRACK_BONE = MOHO.MSG_BASE + 4
MR_TrackBone.APPLY_TRANSFORMATION = MOHO.MSG_BASE + 5
MR_TrackBone.PASTE_FROM_CUR_FRAME = MOHO.MSG_BASE + 6
MR_TrackBone.INTERVAL_1 = MOHO.MSG_BASE + 7
MR_TrackBone.INTERVAL_2 = MOHO.MSG_BASE + 8
MR_TrackBone.INTERVAL_3 = MOHO.MSG_BASE + 9
MR_TrackBone.INTERVAL_4 = MOHO.MSG_BASE + 10
MR_TrackBone.SET_PROJECT_RANGE = MOHO.MSG_BASE + 11
MR_TrackBone.SET_PLAYBACK_RANGE = MOHO.MSG_BASE + 12
MR_TrackBone.TRACK_ANGLE = MOHO.MSG_BASE + 13
MR_TrackBone.MAINTAIN_ANGLE_OFFSET = MOHO.MSG_BASE + 14
MR_TrackBone.MAINTAIN_POS_OFFSET = MOHO.MSG_BASE + 15
MR_TrackBone.COL_PLAIN = MOHO.MSG_BASE + 16
MR_TrackBone.COL_RED = MOHO.MSG_BASE + 17
MR_TrackBone.COL_ORANGE = MOHO.MSG_BASE + 18
MR_TrackBone.COL_YELLOW = MOHO.MSG_BASE + 19
MR_TrackBone.COL_GREEN = MOHO.MSG_BASE + 20
MR_TrackBone.COL_BLUE = MOHO.MSG_BASE + 21
MR_TrackBone.COL_PURPLE = MOHO.MSG_BASE + 22
MR_TrackBone.COL_TAN = MOHO.MSG_BASE + 23
MR_TrackBone.COL_PINK = MOHO.MSG_BASE + 24
MR_TrackBone.COL_TURQUOISE = MOHO.MSG_BASE + 25
MR_TrackBone.COL_CADETBLUE = MOHO.MSG_BASE + 26
MR_TrackBone.COL_CORAL = MOHO.MSG_BASE + 27
MR_TrackBone.RECOLOR_KEYFRAMES = MOHO.MSG_BASE + 28

function MR_TrackBone:DoLayout(moho, layout)
	self.trackAngleCheckbox = LM.GUI.CheckBox(self:Localize('Track Angle'), self.TRACK_ANGLE)
	layout:AddChild(self.trackAngleCheckbox, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

	self.frameRangeCheckbox = LM.GUI.CheckBox(self:Localize('Frame Range'), self.FRAME_RANGE)
	layout:AddChild(self.frameRangeCheckbox, LM.GUI.ALIGN_LEFT, 0)

	self.fromInput = LM.GUI.TextControl(0, '10000', self.FROM, LM.GUI.FIELD_INT, self:Localize('From:'))
	layout:AddChild(self.fromInput, LM.GUI.ALIGN_LEFT, 0)

	self.toInput = LM.GUI.TextControl(0, '10000', self.TO, LM.GUI.FIELD_INT, self:Localize('To:'))
	layout:AddChild(self.toInput, LM.GUI.ALIGN_LEFT, 0)
	
	self.setProjectRangeButton = LM.GUI.ImageButton("ScriptResources/mr_track_bone/mr_set_project_range",self:Localize('Set Project Range'),false, self.SET_PROJECT_RANGE, false)
	layout:AddChild(self.setProjectRangeButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.setPlaybackRangeButton = LM.GUI.ImageButton("ScriptResources/mr_track_bone/mr_set_playback_range",self:Localize('Set Playback Range'),false, self.SET_PLAYBACK_RANGE, false)
	layout:AddChild(self.setPlaybackRangeButton, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddChild(LM.GUI.StaticText(self:Localize("IntervalText")))

	self.intervalMenu = LM.GUI.Menu(MOHO.Localize("Interval=Interval"))
	self.intervalMenu:AddItem(MOHO.Localize("1=1"), 0, self.INTERVAL_1)
	self.intervalMenu:AddItemAlphabetically(MOHO.Localize("2=2"), 0, self.INTERVAL_2)
	self.intervalMenu:AddItemAlphabetically(MOHO.Localize("3=3"), 0, self.INTERVAL_3)
	self.intervalMenu:AddItemAlphabetically(MOHO.Localize("4=4"), 0, self.INTERVAL_4)

	self.intervalPopup = LM.GUI.PopupMenu(50, true)
	self.intervalPopup:SetMenu(self.intervalMenu)
	layout:AddChild(self.intervalPopup)

	self.trackBoneButton = LM.GUI.Button(self:Localize('Track Bone'), self.TRACK_BONE)
	layout:AddChild(self.trackBoneButton, LM.GUI.ALIGN_LEFT, 0)

	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.maintainPositionOffsetCheckbox = LM.GUI.ImageButton("ScriptResources/mr_track_bone/mr_maintain_position_offset",self:Localize('Maintain Position Offset'),true, self.MAINTAIN_POS_OFFSET,false)
	layout:AddChild(self.maintainPositionOffsetCheckbox, LM.GUI.ALIGN_LEFT, 0)
	
	self.maintainAngleOffsetCheckbox = LM.GUI.ImageButton("ScriptResources/mr_track_bone/mr_maintain_angle_offset",self:Localize('Maintain Angle Offset'), true, self.MAINTAIN_ANGLE_OFFSET, false)
	layout:AddChild(self.maintainAngleOffsetCheckbox, LM.GUI.ALIGN_LEFT, 0)

	self.pasteFromCurFrameCheckbox = LM.GUI.ImageButton("ScriptResources/mr_track_bone/mr_paste_from_current_frame",self:Localize('Paste from current frame'), true, self.PASTE_FROM_CUR_FRAME, false)
	layout:AddChild(self.pasteFromCurFrameCheckbox, LM.GUI.ALIGN_LEFT, 0)
	
	self.recolorKeyframesCheckbox = LM.GUI.CheckBox(self:Localize('Recolor Keyframes'), self.RECOLOR_KEYFRAMES)
	layout:AddChild(self.recolorKeyframesCheckbox, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddChild(LM.GUI.StaticText(self:Localize('Color')))

	self.colorMenu = LM.GUI.Menu(MOHO.Localize("KeyframesColor"))
	self.colorMenu:AddItem(self:Localize("Purple"), 0, self.COL_PURPLE)
	self.colorMenu:AddItemAlphabetically(self:Localize("Blue"), 0, self.COL_BLUE)
	self.colorMenu:AddItemAlphabetically(self:Localize("Green"), 0, self.COL_GREEN)
	self.colorMenu:AddItemAlphabetically(self:Localize("Yellow"), 0, self.COL_YELLOW)
	self.colorMenu:AddItemAlphabetically(self:Localize("Orange"), 0, self.COL_ORANGE)
	self.colorMenu:AddItemAlphabetically(self:Localize("Red"), 0, self.COL_RED)
	self.colorMenu:AddItemAlphabetically(self:Localize("Tan"), 0, self.COL_TAN)
	self.colorMenu:AddItemAlphabetically(self:Localize("Pink"), 0, self.COL_PINK)
	self.colorMenu:AddItemAlphabetically(self:Localize("Turquoise"), 0, self.COL_TURQUOISE)
	self.colorMenu:AddItemAlphabetically(self:Localize("CadetBlue"), 0, self.COL_CADETBLUE)
	self.colorMenu:AddItemAlphabetically(self:Localize("Coral"), 0, self.COL_CORAL)

	self.colorMenu:InsertItem(0, "", 0, 0)
	self.colorMenu:InsertItem(0, self:Localize("Plain"), 0, self.COL_PLAIN)

	self.colorPopup = LM.GUI.PopupMenu(120, true)
	self.colorPopup:SetMenu(self.colorMenu)
	layout:AddChild(self.colorPopup)

	self.applyTransformationButton = LM.GUI.Button(self:Localize('Apply Transformation'), self.APPLY_TRANSFORMATION)
	layout:AddChild(self.applyTransformationButton, LM.GUI.ALIGN_LEFT, 0)
end

function MR_TrackBone:OnMouseDown(moho, mouseEvent)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	
	local id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true)
	skel:SelectNone()
	if id ~= -1 then
		skel:Bone(id).fSelected = true
	end
	self:UpdateWidgets(moho)
end

function MR_TrackBone:TrackBone(moho)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	
	local selBone
	
	if (moho:CountSelectedBones() == 1) then
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if bone.fSelected then
				selBone = bone
				break
			end
		end
	else
		return
	end
	
	local isSoundtrackMuted = false
	if not MOHO.MohoGlobals.MuteSoundtrack then
		isSoundtrackMuted = true
		MOHO.MohoGlobals.MuteSoundtrack = true
	end
	
	self.frames = {}
	self.globalPos = {}
	self.globalPosSecond = {}

	local targetFrame = self.from
	local curFrame = moho.frame
	if self.frameRange then
		repeat
			moho:SetCurFrame(targetFrame)
			if self.trackAngle then
				table.insert(self.globalPosSecond, self:GetSecondGlobalPos(moho, selBone))
			end
			
			table.insert(self.frames, targetFrame)
			table.insert(self.globalPos, self:GetGlobalPos(moho, selBone))
			targetFrame = targetFrame + self.interval
		until targetFrame > self.to
		
		if self.frames[#self.frames] < self.to then
			moho:SetCurFrame(self.to)
			if self.trackAngle then
				table.insert(self.globalPosSecond, self:GetSecondGlobalPos(moho, selBone))
			end
			table.insert(self.frames, self.to)
			table.insert(self.globalPos, self:GetGlobalPos(moho, selBone))
		end

	else
		if self.trackAngle then
			table.insert(self.globalPosSecond, self:GetSecondGlobalPos(moho, selBone))
		end
		table.insert(self.frames, curFrame)
		table.insert(self.globalPos, self:GetGlobalPos(moho, selBone))
	end
	if self.frameRange then
		moho:SetCurFrame(curFrame)
	end
	
	self.copiedInterval = self.interval
	self.isReady = true
	
	
	if isSoundtrackMuted then
		MOHO.MohoGlobals.MuteSoundtrack = false
	end
	
	self:UpdateWidgets(moho)
end

function MR_TrackBone:GetGlobalPos(moho, bone)
	local skel = moho:Skeleton()
	local selBonePos = LM.Vector2:new_local()
	
	selBonePos:Set(bone.fPos)
	
	if bone.fParent >-1 then
		local selBoneParentMatrix = LM.Matrix:new_local()
		selBoneParentMatrix:Set(skel:Bone(bone.fParent).fMovedMatrix)
		selBoneParentMatrix:Transform(selBonePos)
	end
	local selLayer = moho.layer
	local selLayerMatrix = LM.Matrix:new_local()
	selLayer:GetFullTransform(moho.frame, selLayerMatrix, nil)
	selLayerMatrix:Transform(selBonePos)
	return selBonePos
end

function MR_TrackBone:GetSecondGlobalPos(moho, bone)
	local skel = moho:Skeleton()
	local offstY = LM.Vector2:new_local()
	offstY:Set(0.2,0)
	
	local selBoneMatrix = LM.Matrix:new_local()
	selBoneMatrix:Set(bone.fMovedMatrix)
	selBoneMatrix:Transform(offstY)

	local selLayer = moho.layer
	local selLayerMatrix = LM.Matrix:new_local()
	selLayer:GetFullTransform(moho.frame, selLayerMatrix, nil)
	selLayerMatrix:Transform(offstY)
	return offstY
end


function MR_TrackBone:ApplyTransformationToBone(moho)
	local skel = moho:Skeleton()
	if (skel == nil) then
		return
	end
	
	local selBone
	
	if (moho:CountSelectedBones() == 1) then
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if bone.fSelected then
				selBone = bone
				break
			end
		end
	else
		return
	end
	
	moho.document:PrepUndo(moho.layer)
	moho.document:SetDirty()
	
	local isSoundtrackMuted = false
	if not MOHO.MohoGlobals.MuteSoundtrack then
		isSoundtrackMuted = true
		MOHO.MohoGlobals.MuteSoundtrack = true
	end
	
	local curFrame = moho.frame
	local keyInterp = MOHO.InterpSetting:new_local()
	keyInterp.interval = self.copiedInterval
	if self.recolorKeyframes then
		keyInterp.tags = self.colorTag
	end
	local startframe = self.from
	local framesDif
	if self.pasteFromCurFrame then
		if self.frameRange then
			framesDif = startframe - curFrame
			startframe = startframe - framesDif
		else
			framesDif = self.frames[1] - curFrame
			startframe = startframe - framesDif
		end
	end
	local posOffset = nil
	local angleOffset = nil
	local angleDif = nil
	local range = self.to - self.from
	local localStartFrame = moho.layer:DocToLayerFrame(startframe)
	local lastAngle = selBone.fAnimAngle:GetValue(localStartFrame)
	
	
	if self.maintainPositionOffset then
		if self.frameRange then
			for i = 1, range do
				selBone.fAnimPos:DeleteKey(localStartFrame + i)
			end
		end	
	else
		if self.frameRange then
			for i = 0, range do
				selBone.fAnimPos:DeleteKey(localStartFrame + i)
			end
		end	
	end
	
	if self.trackAngle then
		if self.maintainAngleOffset then
			if self.frameRange then
				for i = 1, range do
					selBone.fAnimAngle:DeleteKey(localStartFrame + i)
				end
			end	
		else
			if self.frameRange then
				for i = 0, range do
					selBone.fAnimAngle:DeleteKey(localStartFrame + i)
				end
			end	
		end	
	end
	
	for i, a in pairs(self.frames) do
		if self.pasteFromCurFrame and not self.frameRange then
		else
			if self.pasteFromCurFrame then
				moho:SetCurFrame(a - framesDif)
			else
				moho:SetCurFrame(a)
			end
		end
		
		local posDelta = LM.Vector2:new_local()
		posDelta = selBone.fPos - selBone.fAnimPos:GetValue(moho.layerFrame)
		local globalPos = LM.Vector2:new_local()
		local globalPosSecond = LM.Vector2:new_local()
		local localPos = LM.Vector2:new_local()
		local localPosSecond = LM.Vector2:new_local()
		globalPos:Set(self.globalPos[i])
		if self.maintainPositionOffset and posOffset == nil then
			local curGlobalPos = self:GetGlobalPos(moho, selBone)
			posOffset = curGlobalPos - globalPos
		end
		
		if self.maintainPositionOffset and posOffset ~= nil then
			globalPos = globalPos + posOffset
		end
		localPos:Set(self:GetLocalPos(moho, selBone, globalPos))
		localPos:Set(localPos - posDelta)
		
		selBone.fAnimPos:SetValue(moho.layerFrame, localPos)
		selBone.fAnimPos:SetKeyInterp(moho.layerFrame, keyInterp)
		
		if self.trackAngle then
			local angleDelta = selBone.fAngle - selBone.fAnimAngle:GetValue(moho.layerFrame)
			globalPosSecond:Set(self.globalPosSecond[i])
			if self.maintainPositionOffset and posOffset ~= nil then
			
				globalPosSecond = globalPosSecond + posOffset
			end
			
			localPosSecond:Set(self:GetLocalPos(moho, selBone, globalPosSecond))
			localPosSecond:Set(localPosSecond - posDelta)
			local vectorPos = localPosSecond - localPos
			local angle = math.atan2(vectorPos.y, vectorPos.x)
			angle = angle - angleDelta
			
			if self.trackAngle and self.maintainAngleOffset and angleOffset == nil then
				angleOffset = selBone.fAngle - angle
			elseif self.trackAngle and not self.maintainAngleOffset and angleDif == nil then
				local keyID = selBone.fAnimAngle:GetClosestKeyID(moho.layerFrame)
				angleDif = selBone.fAnimAngle:GetValue(selBone.fAnimAngle:GetKeyWhen(keyID))
				local tempDif = 0
				if angleDif > (math.pi * 2) then
					repeat
						angleDif = angleDif - (math.pi * 2)
						tempDif = tempDif + (math.pi * 2)
					until angleDif <= 0
				elseif angleDif < 0 then
					repeat
						angleDif = angleDif + (math.pi * 2)
						tempDif = tempDif - (math.pi * 2)
					until angleDif >= -math.pi
				end
				angleDif = tempDif
			end
			
			if self.trackAngle and self.maintainAngleOffset and angleOffset ~= nil then
				angle = angle + angleOffset
			elseif self.trackAngle and not self.maintainAngleOffset and angleDif ~= nil then
				angle = angle + angleDif
			end

			if angle > lastAngle + math.pi then
				if i > 1 then
					repeat
						angle = angle - (math.pi * 2)
					until angle < lastAngle + math.pi
				end	
			end
			
			if angle < lastAngle - math.pi then
				if i > 1 then
					repeat
						angle = angle + (math.pi * 2)
					until angle > lastAngle - math.pi
				end	
			end
			lastAngle = angle
			selBone.fAnimAngle:SetValue(moho.layerFrame, angle)
			selBone.fAnimAngle:SetKeyInterp(moho.layerFrame, keyInterp)
		end
	end
	
	if isSoundtrackMuted then
		MOHO.MohoGlobals.MuteSoundtrack = false
	end
	
	moho:SetCurFrame(curFrame)
	moho:UpdateSelectedChannels()
	moho.view:DrawMe()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
end

function MR_TrackBone:GetLocalPos(moho, bone, globalPos)
	local skel = moho:Skeleton()
	local localPos = LM.Vector2:new_local()
	localPos:Set(globalPos)
	
	local selLayer = moho.layer
	local selLayerMatrix = LM.Matrix:new_local()
	selLayer:GetFullTransform(moho.frame, selLayerMatrix, nil)
	selLayerMatrix:Invert()
	selLayerMatrix:Transform(localPos)
	if bone.fParent >-1 then
		local selBoneParentMatrix = LM.Matrix:new_local()
		selBoneParentMatrix:Set(skel:Bone(bone.fParent).fMovedMatrix)
		selBoneParentMatrix:Invert()
		selBoneParentMatrix:Transform(localPos)
	end
	return localPos
end

function MR_TrackBone:UpdateWidgets(moho)
	self.trackAngleCheckbox:SetValue(self.trackAngle)
	self.frameRangeCheckbox:SetValue(self.frameRange)
	self.fromInput:SetValue(self.from)
	self.toInput:SetValue(self.to)
	self.pasteFromCurFrameCheckbox:SetValue(self.pasteFromCurFrame)
	self.maintainPositionOffsetCheckbox:SetValue(self.maintainPositionOffset)
	self.maintainAngleOffsetCheckbox:SetValue(self.maintainAngleOffset)
	self.maintainAngleOffsetCheckbox:Enable(self.trackAngle)
	self.recolorKeyframesCheckbox:SetValue(self.recolorKeyframes)
	
	if self.interval < 1 or self.interval > 4 or self.interval == nil then
		self.interval = 1
	end
	
	if (self.interval == 1) then
		self.intervalMenu:SetChecked(self.INTERVAL_1, true)
	elseif (self.interval == 2) then
		self.intervalMenu:SetChecked(self.INTERVAL_2, true)
	elseif (self.interval == 3) then
		self.intervalMenu:SetChecked(self.INTERVAL_3, true)
	elseif (self.interval == 4) then
		self.intervalMenu:SetChecked(self.INTERVAL_4, true)
	end

	self.intervalPopup:Enable(self.frameRange)
	self.intervalPopup:Redraw()
	
	self.fromInput:Enable(self.frameRange)
	self.toInput:Enable(self.frameRange)
	if (moho:CountSelectedBones() ~= 1) then
		self.trackBoneButton:Enable(false)
		self.applyTransformationButton:Enable(false)
	else	
		self.trackBoneButton:Enable(true)
		self.applyTransformationButton:Enable(self.isReady)
	end
	
	self.setProjectRangeButton:Enable(self.frameRange)
	self.setPlaybackRangeButton:Enable(self.frameRange)
	
	if (self.colorTag == 0) then
		self.colorMenu:SetChecked(self.COL_PLAIN, true)
	elseif (self.colorTag == 1) then
		self.colorMenu:SetChecked(self.COL_RED, true)	
	elseif (self.colorTag == 2) then
		self.colorMenu:SetChecked(self.COL_ORANGE, true)
	elseif (self.colorTag == 3) then
		self.colorMenu:SetChecked(self.COL_YELLOW, true)
	elseif (self.colorTag == 4) then
		self.colorMenu:SetChecked(self.COL_GREEN, true)
	elseif (self.colorTag == 5) then
		self.colorMenu:SetChecked(self.COL_BLUE, true)
	elseif (self.colorTag == 6) then
		self.colorMenu:SetChecked(self.COL_PURPLE, true)
	elseif (self.colorTag == 7) then
		self.colorMenu:SetChecked(self.COL_TAN, true)
	elseif (self.colorTag == 8) then
		self.colorMenu:SetChecked(self.COL_PINK, true)
	elseif (self.colorTag == 9) then
		self.colorMenu:SetChecked(self.COL_TURQUOISE, true)
	elseif (self.colorTag == 10) then
		self.colorMenu:SetChecked(self.COL_CADETBLUE, true)
	elseif (self.colorTag == 11) then
		self.colorMenu:SetChecked(self.COL_CORAL, true)
	end

	self.colorPopup:Enable(self.recolorKeyframes)
	self.colorPopup:Redraw()
end

function MR_TrackBone:HandleMessage(moho, view, msg)
	if msg == self.TRACK_ANGLE then
		self.trackAngle = self.trackAngleCheckbox:Value()
		self.isReady = false
		self:UpdateWidgets(moho)
	elseif msg == self.FRAME_RANGE then
		self.frameRange = self.frameRangeCheckbox:Value()
		self.isReady = false
		self:UpdateWidgets(moho)		
	elseif msg == self.FROM then
		self.from = self.fromInput:IntValue()
		if self.from < 1 then
			self.from = 1
		end	
		if self.to < self.from then
			self.to = self.from
		end
		self.isReady = false
		moho:UpdateUI()
	elseif msg == self.TO then
		self.to = self.toInput:IntValue()
		if self.to < self.from then
			if self.to < 1 then
				self.to = 1
			end
			self.from = self.to
		end
		self.isReady = false
		moho:UpdateUI()
	elseif msg == self.SET_PROJECT_RANGE then
		self.from = moho.document:StartFrame()
		self.to = moho.document:EndFrame()
		self.isReady = false
		moho:UpdateUI()	
	elseif msg == self.SET_PLAYBACK_RANGE then
		local startP = MOHO.MohoGlobals.PlayStart
		local endP = MOHO.MohoGlobals.PlayEnd
		if startP > 0 then
			self.from = startP
		end
		if endP > 0 then
			self.to = endP
		end

		if MOHO.MohoGlobals.PlayStart > 0 or MOHO.MohoGlobals.PlayEnd > 0 then
			self.isReady = false
		end	
		moho:UpdateUI()	
	elseif (msg >= self.INTERVAL_1 and msg <= self.INTERVAL_4) then
		local int = 1
		if (msg == self.INTERVAL_1) then
			int = 1
		elseif (msg == self.INTERVAL_2) then
			int = 2
		elseif (msg == self.INTERVAL_3) then
			int = 3
		elseif (msg == self.INTERVAL_4) then
			int = 4
		end	
		self.interval = int
		self.isReady = false
		self:UpdateWidgets(moho)
	elseif msg == self.TRACK_BONE then
		self:TrackBone(moho)
	elseif msg == self.MAINTAIN_POS_OFFSET then
		self.maintainPositionOffset = self.maintainPositionOffsetCheckbox:Value()		
	elseif msg == self.MAINTAIN_ANGLE_OFFSET then
		self.maintainAngleOffset = self.maintainAngleOffsetCheckbox:Value()	
	elseif msg == self.PASTE_FROM_CUR_FRAME then
		self.pasteFromCurFrame = self.pasteFromCurFrameCheckbox:Value()
	elseif msg == self.RECOLOR_KEYFRAMES then
		self.recolorKeyframes = self.recolorKeyframesCheckbox:Value()
		self:UpdateWidgets(moho)		
	elseif (msg >= self.COL_PLAIN and msg <= self.COL_CORAL) then
		local tag = 0
		if (msg == self.COL_RED) then
			tag = 1
		elseif (msg == self.COL_ORANGE) then
			tag = 2
		elseif (msg == self.COL_YELLOW) then
			tag = 3
		elseif (msg == self.COL_GREEN) then
			tag = 4
		elseif (msg == self.COL_BLUE) then
			tag = 5
		elseif (msg == self.COL_PURPLE) then
			tag = 6
		elseif (msg == self.COL_TAN) then
			tag = 7
		elseif (msg == self.COL_PINK) then
			tag = 8
		elseif (msg == self.COL_TURQUOISE) then
			tag = 9
		elseif (msg == self.COL_CADETBLUE) then
			tag = 10
		elseif (msg == self.COL_CORAL) then
			tag = 11
		end	
		self.colorTag = tag	
	elseif msg == self.APPLY_TRANSFORMATION then
		self:ApplyTransformationToBone(moho)
	end
end

-- **************************************************
-- Localization
-- **************************************************

function MR_TrackBone:Localize(text)
	local phrase = {}

	phrase['Description'] = 'This tool allows you to track a bone globally and take its transformation to any other bone of any skeleton. You can get both the position and the rotation.'
	phrase['UILabel'] = 'Track Bone'

	phrase['Track Angle'] = 'Track Angle'
	phrase['Frame Range'] = 'Frame Range'
	phrase['From:'] = 'From:'
	phrase['To:'] = 'To:'
	phrase['Set Project Range'] = 'Set Project Range'
	phrase['Set Playback Range'] = 'Set Playback Range'
	phrase['IntervalText'] = 'Interval:'
	phrase['Track Bone'] = 'Track Bone'
	phrase['Maintain Position Offset'] = 'Maintain Position Offset'
	phrase['Maintain Angle Offset'] = 'Maintain Angle Offset'
	phrase['Paste from current frame'] = 'Paste From Current Frame'
	phrase['Recolor Keyframes'] = 'Recolor Keyframes'
	phrase['Color'] = 'Color:'
	phrase['KeyframesColor'] = 'Keyframes Color'
	phrase['Apply Transformation'] = 'Apply Transformation'
	phrase['Plain'] = 'Plain'
	phrase['Red'] = 'Red'
	phrase['Orange'] = 'Orange'
	phrase['Yellow'] = 'Yellow'
	phrase['Green'] = 'Green'
	phrase['Blue'] = 'Blue'
	phrase['Purple'] = 'Purple'
	phrase['Tan'] = 'Tan'
	phrase['Pink'] = 'Pink'
	phrase['Turquoise'] = 'Turquoise'
	phrase['CadetBlue'] = 'CadetBlue'
	phrase['Coral'] = 'Coral'

	local fileWord = MOHO.Localize("/Menus/File/File=File")
	if fileWord == "Файл" then
		phrase['Description'] = 'Этот инструмент позволяет сделать глобальный трекинг кости и применить её трансформацию на любую другую кость любого скелета. Можно передать как позицию, так и поворот кости.'
		phrase['UILabel'] = 'Отследить кость'
		
		phrase['Track Angle'] = 'Отследить угол'
		phrase['Frame Range'] = 'Диапазон кадров'
		phrase['From:'] = 'От:'
		phrase['To:'] = 'До:'
		phrase['Set Project Range'] = 'Использовать диапазон проекта'
		phrase['Set Playback Range'] = 'Использовать диапазон проигрывания'
		phrase['IntervalText'] = 'Интервал:'
		phrase['Track Bone'] = 'Отследить кость'
		phrase['Maintain Position Offset'] = 'Соблюдать смещение позиции'
		phrase['Maintain Angle Offset'] = 'Соблюдать смещение угла'
		phrase['Paste from current frame'] = 'Вставить с текущего кадра'
		phrase['Recolor Keyframes'] = 'Перекрасить ключевые кадры'
		phrase['Color'] = 'Цвет:'
		phrase['KeyframesColor'] = 'Цвет ключевых кадров'
		phrase['Apply Transformation'] = 'Применить трансформацию'
		phrase['Plain'] = 'Обычный'
		phrase['Red'] = 'Красный'
		phrase['Orange'] = 'Оранжевый'
		phrase['Yellow'] = 'Желтый'
		phrase['Green'] = 'Зеленый'
		phrase['Blue'] = 'Синий'
		phrase['Purple'] = 'Лиловый'
		phrase['Tan'] = 'Кремовый'
		phrase['Pink'] = 'Розовый'
		phrase['Turquoise'] = 'Берюзовый'
		phrase['CadetBlue'] = 'Серо-синий'
		phrase['Coral'] = 'Коралловый'
	end

	return phrase[text]
end

Icon
MR Track Bone
Listed

Script type: Tool

Uploaded: May 09 2021, 09:49

This script allows you to track a bone globally and take its transformation to any other bone of any skeleton.


The script is compatible with Moho 12 and 13.5.
This script, and all other scripts on this site are distributed as free software under the GNU General Public License 3.0 or later.
Downloads count: 241