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

ScriptName = "MR_KeyMotion"

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

MR_KeyMotion = {}

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

function MR_KeyMotion:Version()
	return '1.1'
end

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

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

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

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

MR_KeyMotion.smooth = true
MR_KeyMotion.pull = false
MR_KeyMotion.scale = false
MR_KeyMotion.flip = false
MR_KeyMotion.centerTop = true
MR_KeyMotion.centerMid = false
MR_KeyMotion.centerDown = false
MR_KeyMotion.scaleIn = true
MR_KeyMotion.useChannelX = true
MR_KeyMotion.useChannelY = true
MR_KeyMotion.useChannelZ = true
MR_KeyMotion.scaleValue = 10
MR_KeyMotion.smoothFrameOffset = 4
MR_KeyMotion.smoothStepValue = 4
MR_KeyMotion.smoothThresholdValue = 4
MR_KeyMotion.allowDifference = 0.005
MR_KeyMotion.pullFrameOffset = 2
MR_KeyMotion.pullStepValue = 8
MR_KeyMotion.mohoVersion = 0

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

function MR_KeyMotion:LoadPrefs(prefs)
	self.smooth = prefs:GetBool("MR_KeyMotion.smooth", true)
    self.pull = prefs:GetBool("MR_KeyMotion.pull", false)
    self.scale = prefs:GetBool("MR_KeyMotion.scale", false)
    self.flip = prefs:GetBool("MR_KeyMotion.flip", false)
	self.centerTop = prefs:GetBool("MR_KeyMotion.centerTop", true)
	self.centerMid = prefs:GetBool("MR_KeyMotion.centerMid", false)
	self.centerDown = prefs:GetBool("MR_KeyMotion.centerDown", false)
	self.scaleIn = prefs:GetBool("MR_KeyMotion.scaleIn", true)
	self.scaleOut = prefs:GetBool("MR_KeyMotion.scaleOut", false)
	self.useChannelX = prefs:GetBool("MR_KeyMotion.useChannelX", true)
	self.useChannelY = prefs:GetBool("MR_KeyMotion.useChannelY", true)
	self.useChannelZ = prefs:GetBool("MR_KeyMotion.useChannelZ", true)
	self.scaleValue = prefs:GetInt("MR_KeyMotion.scaleValue", 10)
	self.smoothStepValue = prefs:GetInt("MR_KeyMotion.smoothStepValue", 4)
	self.pullStepValue = prefs:GetInt("MR_KeyMotion.pullStepValue", 8)
end

function MR_KeyMotion:SavePrefs(prefs)
	prefs:SetBool("MR_KeyMotion.smooth", self.smooth)
    prefs:SetBool("MR_KeyMotion.pull", self.pull)
    prefs:SetBool("MR_KeyMotion.scale", self.scale)
    prefs:SetBool("MR_KeyMotion.flip", self.flip)
	prefs:SetBool("MR_KeyMotion.centerTop", self.centerTop)
	prefs:SetBool("MR_KeyMotion.centerMid", self.centerMid)
	prefs:SetBool("MR_KeyMotion.centerDown", self.centerDown)
	prefs:SetBool("MR_KeyMotion.scaleIn", self.scaleIn)
	prefs:SetBool("MR_KeyMotion.scaleOut", self.scaleOut)
	prefs:SetBool("MR_KeyMotion.useChannelX", self.useChannelX)
	prefs:SetBool("MR_KeyMotion.useChannelY", self.useChannelY)
	prefs:SetBool("MR_KeyMotion.useChannelZ", self.useChannelZ)
	prefs:SetInt("MR_KeyMotion.scaleValue", self.scaleValue)
	prefs:SetInt("MR_KeyMotion.scaleValue", self.smoothStepValue)
	prefs:SetInt("MR_KeyMotion.pullStepValue", self.pullStepValue)
end

function MR_KeyMotion:ResetPrefs()
	MR_KeyMotion.smooth = true
    MR_KeyMotion.pull = false
    MR_KeyMotion.scale = false
    MR_KeyMotion.flip = false
	MR_KeyMotion.centerTop = true
	MR_KeyMotion.centerMid = false
	MR_KeyMotion.centerDown = false
	MR_KeyMotion.scaleIn = true
	MR_KeyMotion.scaleOut = false
	MR_KeyMotion.useChannelX = true
	MR_KeyMotion.useChannelY = true
	MR_KeyMotion.useChannelZ = true
	MR_KeyMotion.scaleValue = 10
	MR_KeyMotion.smoothStepValue = 4
	MR_KeyMotion.pullStepValue = 8
end

-- **************************************************
-- MR_KeyMotionDialog
-- **************************************************

local MR_KeyMotionDialog = {}

MR_KeyMotionDialog.SMOOTH = MOHO.MSG_BASE
MR_KeyMotionDialog.PULL = MOHO.MSG_BASE + 1
MR_KeyMotionDialog.SCALE = MOHO.MSG_BASE + 2
MR_KeyMotionDialog.FLIP = MOHO.MSG_BASE + 3
MR_KeyMotionDialog.CENTER_TOP_BUTTON = MOHO.MSG_BASE + 4
MR_KeyMotionDialog.CENTER_MID_BUTTON = MOHO.MSG_BASE + 5
MR_KeyMotionDialog.CENTER_DOWN_BUTTON = MOHO.MSG_BASE + 6
MR_KeyMotionDialog.SCALE_IN_BUTTON = MOHO.MSG_BASE + 7
MR_KeyMotionDialog.SCALE_OUT_BUTTON = MOHO.MSG_BASE + 8
MR_KeyMotionDialog.USE_CHANNEL_X = MOHO.MSG_BASE + 9
MR_KeyMotionDialog.USE_CHANNEL_Y = MOHO.MSG_BASE + 10
MR_KeyMotionDialog.USE_CHANNEL_Z = MOHO.MSG_BASE + 11
MR_KeyMotionDialog.SCALE_PERCENTAGE = MOHO.MSG_BASE + 12
MR_KeyMotionDialog.SMOOTH_PERCENTAGE = MOHO.MSG_BASE + 13
MR_KeyMotionDialog.PULL_PERCENTAGE = MOHO.MSG_BASE + 14

function MR_KeyMotionDialog:new()
	local d = LM.GUI.SimpleDialog(MR_KeyMotion:Localize('UILabel'), MR_KeyMotionDialog)
	local l = d:GetLayout()
	
	l:PushH()
		d.smoothRadioButton = LM.GUI.RadioButton(MR_KeyMotion:Localize('Smooth'), d.SMOOTH)
        l:AddChild(d.smoothRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		l:AddPadding(-5)
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

		l:AddPadding(-5)

        d.pullRadioButton = LM.GUI.RadioButton(MR_KeyMotion:Localize('Pull'), d.PULL)
        l:AddChild(d.pullRadioButton, LM.GUI.ALIGN_LEFT, 0)

		l:AddPadding(-5)

		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

		l:AddPadding(-5)

        d.scaleRadioButton = LM.GUI.RadioButton(MR_KeyMotion:Localize('Scale'), d.SCALE)
        l:AddChild(d.scaleRadioButton, LM.GUI.ALIGN_LEFT, 0)

		l:AddPadding(-5)

		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
		
		l:AddPadding(-5)
		
        d.flipRadioButton = LM.GUI.RadioButton(MR_KeyMotion:Localize('Flip'), d.FLIP)
        l:AddChild(d.flipRadioButton, LM.GUI.ALIGN_LEFT, 0)
	l:Pop()
	
	l:PushH()
		d.smoothPercentageInput = LM.GUI.TextControl(84, '10', d.SMOOTH_PERCENTAGE, LM.GUI.FIELD_INT, MR_KeyMotion:Localize('Smooth Percentage'))
		l:AddChild(d.smoothPercentageInput, LM.GUI.ALIGN_LEFT, 0)
		
		d.pullPercentageInput = LM.GUI.TextControl(84, '10', d.PULL_PERCENTAGE, LM.GUI.FIELD_INT, MR_KeyMotion:Localize('Pull Percentage'))
		l:AddChild(d.pullPercentageInput, LM.GUI.ALIGN_LEFT, 0)
		
		d.scalePercentageInput = LM.GUI.TextControl(84, '10', d.SCALE_PERCENTAGE, LM.GUI.FIELD_INT, MR_KeyMotion:Localize('Scale Percentage'))
		l:AddChild(d.scalePercentageInput, LM.GUI.ALIGN_LEFT, 0)
	l:Pop()
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
	l:PushH()
		d.useChanneXButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_transform_x', MR_KeyMotion:Localize('Transform X'), true, d.USE_CHANNEL_X, false)
		l:AddChild(d.useChanneXButton, LM.GUI.ALIGN_LEFT, 0)

		d.useChanneYButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_transform_y', MR_KeyMotion:Localize('Transform Y'), true, d.USE_CHANNEL_Y, false)
		l:AddChild(d.useChanneYButton, LM.GUI.ALIGN_LEFT, 0)
		
		d.useChanneZButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_transform_z', MR_KeyMotion:Localize('Transform Z'), true, d.USE_CHANNEL_Z, false)
		l:AddChild(d.useChanneZButton, LM.GUI.ALIGN_LEFT, 0)
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
		
		d.centerTopButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_center_top', MR_KeyMotion:Localize('Transform Top'), true, d.CENTER_TOP_BUTTON, false)
		l:AddChild(d.centerTopButton, LM.GUI.ALIGN_LEFT, 0)

		d.centerMidButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_center_mid', MR_KeyMotion:Localize('Transform Mid'), true, d.CENTER_MID_BUTTON, false)
		l:AddChild(d.centerMidButton, LM.GUI.ALIGN_LEFT, 0)

		d.centerDownButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_center_down', MR_KeyMotion:Localize('Transform Down'), true, d.CENTER_DOWN_BUTTON, false)
		l:AddChild(d.centerDownButton, LM.GUI.ALIGN_LEFT, 0)
		
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
		
		d.scaleInButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_scale_in', MR_KeyMotion:Localize('Scale In'), true, d.SCALE_IN_BUTTON, false)
		l:AddChild(d.scaleInButton, LM.GUI.ALIGN_LEFT, 0)

		d.scaleOutButton = LM.GUI.ImageButton('ScriptResources/mr_key_motion/mr_scale_out', MR_KeyMotion:Localize('Scale Out'), true, d.SCALE_OUT_BUTTON, false)
		l:AddChild(d.scaleOutButton, LM.GUI.ALIGN_LEFT, 0)
		
	l:Pop()
	
	return d
end

function MR_KeyMotionDialog:UpdateWidgets(moho)
	self.smoothRadioButton:SetValue(MR_KeyMotion.smooth)
    self.pullRadioButton:SetValue(MR_KeyMotion.pull)
    self.scaleRadioButton:SetValue(MR_KeyMotion.scale)
    self.flipRadioButton:SetValue(MR_KeyMotion.flip)
	self.centerTopButton:SetValue(MR_KeyMotion.centerTop)
	self.centerMidButton:SetValue(MR_KeyMotion.centerMid)
	self.centerDownButton:SetValue(MR_KeyMotion.centerDown)
	self.scaleInButton:SetValue(MR_KeyMotion.scaleIn)
	self.scaleOutButton:SetValue(MR_KeyMotion.scaleOut)
	self.useChanneXButton:SetValue(MR_KeyMotion.useChannelX)
	self.useChanneYButton:SetValue(MR_KeyMotion.useChannelY)
	self.useChanneZButton:SetValue(MR_KeyMotion.useChannelZ)
	self.smoothPercentageInput:SetValue(MR_KeyMotion.smoothStepValue)
	self.pullPercentageInput:SetValue(MR_KeyMotion.pullStepValue)
	self.scalePercentageInput:SetValue(MR_KeyMotion.scaleValue)
	
	self.centerTopButton:Enable(not MR_KeyMotion.smooth and not MR_KeyMotion.pull)
	self.centerMidButton:Enable(not MR_KeyMotion.smooth and not MR_KeyMotion.pull)
	self.centerDownButton:Enable(not MR_KeyMotion.smooth and not MR_KeyMotion.pull)
	self.scaleInButton:Enable(MR_KeyMotion.scale)
	self.scaleOutButton:Enable(MR_KeyMotion.scale)
	self.smoothPercentageInput:Enable(MR_KeyMotion.smooth)
	self.pullPercentageInput:Enable(MR_KeyMotion.pull)
	self.scalePercentageInput:Enable(MR_KeyMotion.scale)
end

function MR_KeyMotionDialog:OnOK(moho)
	MR_KeyMotion.smooth = self.smoothRadioButton:Value()
    MR_KeyMotion.pull = self.pullRadioButton:Value()
    MR_KeyMotion.scale = self.scaleRadioButton:Value()
    MR_KeyMotion.flip = self.flipRadioButton:Value()
	MR_KeyMotion.centerTop = self.centerTopButton:Value()
	MR_KeyMotion.centerMid = self.centerMidButton:Value()
	MR_KeyMotion.centerDown = self.centerDownButton:Value()
	MR_KeyMotion.scaleIn = self.scaleInButton:Value()
	MR_KeyMotion.scaleOut = self.scaleOutButton:Value()
	MR_KeyMotion.useChannelX = self.useChanneXButton:Value()
	MR_KeyMotion.useChannelY = self.useChanneYButton:Value()
	MR_KeyMotion.useChannelZ = self.useChanneZButton:Value()
	MR_KeyMotion.smoothStepValue = self.smoothPercentageInput:IntValue()
	MR_KeyMotion.pullStepValue = self.pullPercentageInput:IntValue()
	MR_KeyMotion.scaleValue = self.scalePercentageInput:IntValue()
end

function MR_KeyMotionDialog:HandleMessage(msg)
	if msg == self.SMOOTH then
		self.centerTopButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerMidButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerDownButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.scaleInButton:Enable(self.scaleRadioButton:Value())
		self.scaleOutButton:Enable(self.scaleRadioButton:Value())
		self.smoothPercentageInput:Enable(self.smoothRadioButton:Value())
		self.pullPercentageInput:Enable(self.pullRadioButton:Value())
		self.scalePercentageInput:Enable(self.scaleRadioButton:Value())
    elseif msg == self.PULL then
		self.centerTopButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerMidButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerDownButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.scaleInButton:Enable(self.scaleRadioButton:Value())
		self.scaleOutButton:Enable(self.scaleRadioButton:Value())
		self.smoothPercentageInput:Enable(self.smoothRadioButton:Value())
		self.pullPercentageInput:Enable(self.pullRadioButton:Value())
		self.scalePercentageInput:Enable(self.scaleRadioButton:Value())
    elseif msg == self.SCALE then
		self.centerTopButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerMidButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerDownButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.scaleInButton:Enable(self.scaleRadioButton:Value())
		self.scaleOutButton:Enable(self.scaleRadioButton:Value())
		self.smoothPercentageInput:Enable(self.smoothRadioButton:Value())
		self.pullPercentageInput:Enable(self.pullRadioButton:Value())
		self.scalePercentageInput:Enable(self.scaleRadioButton:Value())
    elseif msg == self.FLIP then
		self.centerTopButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerMidButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.centerDownButton:Enable(not self.smoothRadioButton:Value() and not self.pullRadioButton:Value())
		self.scaleInButton:Enable(self.scaleRadioButton:Value())
		self.scaleOutButton:Enable(self.scaleRadioButton:Value())
		self.smoothPercentageInput:Enable(self.smoothRadioButton:Value())
		self.pullPercentageInput:Enable(self.pullRadioButton:Value())
		self.scalePercentageInput:Enable(self.scaleRadioButton:Value())
    elseif msg == self.CENTER_TOP_BUTTON then
		self.centerTopButton:SetValue(true)
		self.centerMidButton:SetValue(false)
		self.centerDownButton:SetValue(false)
	elseif msg == self.CENTER_MID_BUTTON then
		self.centerTopButton:SetValue(false)
		self.centerMidButton:SetValue(true)
		self.centerDownButton:SetValue(false)
	elseif msg == self.CENTER_DOWN_BUTTON then
		self.centerTopButton:SetValue(false)
		self.centerMidButton:SetValue(false)
		self.centerDownButton:SetValue(true)
	elseif msg == self.SCALE_IN_BUTTON then
		self.scaleInButton:SetValue(true)
		self.scaleOutButton:SetValue(false)
	elseif msg == self.SCALE_OUT_BUTTON then
		self.scaleInButton:SetValue(false)
		self.scaleOutButton:SetValue(true)
	elseif msg == self.USE_CHANNEL_X then
		if not self.useChanneXButton:Value() then
			if self.useChanneYButton:Value() or self.useChanneZButton:Value() then
				self.useChanneXButton:SetValue(self.useChanneXButton:Value())
			else
				self.useChanneXButton:SetValue(true)
			end
		else
			self.useChanneXButton:SetValue(self.useChanneXButton:Value())
		end
	elseif msg == self.USE_CHANNEL_Y then
		if not self.useChanneYButton:Value() then
			if self.useChanneXButton:Value() or self.useChanneZButton:Value() then
				self.useChanneYButton:SetValue(self.useChanneYButton:Value())
			else
				self.useChanneYButton:SetValue(true)
			end
		else
			self.useChanneYButton:SetValue(self.useChanneYButton:Value())
		end
	elseif msg == self.USE_CHANNEL_Z then
		if not self.useChanneZButton:Value() then
			if self.useChanneXButton:Value() or self.useChanneYButton:Value() then
				self.useChanneZButton:SetValue(self.useChanneZButton:Value())
			else
				self.useChanneZButton:SetValue(true)
			end
		else
			self.useChanneZButton:SetValue(self.useChanneZButton:Value())
		end	
	elseif msg == self.SMOOTH_PERCENTAGE then	
		self.smoothPercentageInput:SetValue(LM.Clamp(self.smoothPercentageInput:Value(), 1, 100))
	elseif msg == self.PULL_PERCENTAGE then	
		self.pullPercentageInput:SetValue(LM.Clamp(self.pullPercentageInput:Value(), 1, 100))
	elseif msg == self.SCALE_PERCENTAGE then	
		self.scalePercentageInput:SetValue(LM.Clamp(self.scalePercentageInput:Value(), 1, 100))
	end
end

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

function MR_KeyMotion:IsRelevant(moho)
	return true
end

function MR_KeyMotion:IsEnabled(moho)
	return true
end

-- **************************************************
-- The guts of this script
-- **************************************************

function MR_KeyMotion:Run(moho)
	local skel = moho:Skeleton()
	local mesh = moho:Mesh()
		
	self.isUndoPrepared = false
	self.isKeysSelected = false

	local v1, v2, v3 = self:GetMohoVersion(moho)
	self.mohoVersion = v1

	if skel then
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if bone.fSelected then
				if MR_KeyMotion.smooth then
					self:AnalizeAndSmoothChanelVal(moho, bone.fAnimAngle)
					self:AnalizeAndSmoothChanelVal(moho, bone.fAnimScale)
					self:AnalizeAndSmoothChanelVec(moho, bone.fAnimPos)
				elseif MR_KeyMotion.pull then	
					self:AnalizeAndPullChanelVal(moho, bone.fAnimAngle)
					self:AnalizeAndPullChanelVal(moho, bone.fAnimScale)
					self:AnalizeAndPullChanelVec(moho, bone.fAnimPos)
				elseif MR_KeyMotion.scale then
					local boneAngleChannel = bone.fAnimAngle
					local boneScaleChannel = bone.fAnimScale
					self:ScaleChannel(moho, boneAngleChannel, false, false)
					self:ScaleChannel(moho, boneScaleChannel, false, false)
					
					if bone.fAnimPos:AreDimensionsSplit() then
						local boneTranslationChannel1 = bone.fAnimPos:DimensionChannel(0)
						local boneTranslationChannel2 = bone.fAnimPos:DimensionChannel(1)
						self:ScaleChannel(moho, boneTranslationChannel1, false, false)
						self:ScaleChannel(moho, boneTranslationChannel2, false, false)
					else
						local boneTranslationChannel1 = bone.fAnimPos
						self:ScaleChannel(moho, boneTranslationChannel1, true, false)
					end
				elseif MR_KeyMotion.flip then
					local boneAngleChannel = bone.fAnimAngle
					local boneScaleChannel = bone.fAnimScale
					self:ScaleChannel(moho, boneAngleChannel, false, true)
					self:ScaleChannel(moho, boneScaleChannel, false, true)
					
					if bone.fAnimPos:AreDimensionsSplit() then
						local boneTranslationChannel1 = bone.fAnimPos:DimensionChannel(0)
						local boneTranslationChannel2 = bone.fAnimPos:DimensionChannel(1)
						self:ScaleChannel(moho, boneTranslationChannel1, false, true)
						self:ScaleChannel(moho, boneTranslationChannel2, false, true)
					else
						local boneTranslationChannel1 = bone.fAnimPos
						self:ScaleChannel(moho, boneTranslationChannel1, true, true)
					end	
				end	
			end	
		end
	elseif mesh then
		for i = 0, mesh:CountPoints() - 1 do
			local point = mesh:Point(i)
			if point.fSelected then
				if MR_KeyMotion.smooth then
					self:AnalizeAndSmoothChanelVec(moho, point.fAnimPos)
					self:AnalizeAndSmoothChanelVal(moho, point.fWidth)
					for c = 0, point:CountCurves() - 1 do
						local myCurve, where = point:Curve(c, -1)
						local curvatureChannel = myCurve:Curvature(where)
						self:AnalizeAndSmoothChanelVal(moho, curvatureChannel)
						if AE_Utilities then
							local curvatureOffsetChannel1 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, false)
							local curvatureOffsetChannel2 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, true)
							local curvatureWeightChannel1 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, false)
							local curvatureWeightChannel2 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, true)
							self:AnalizeAndSmoothChanelVal(moho, curvatureOffsetChannel1)
							self:AnalizeAndSmoothChanelVal(moho, curvatureOffsetChannel2)
							self:AnalizeAndSmoothChanelVal(moho, curvatureWeightChannel1)
							self:AnalizeAndSmoothChanelVal(moho, curvatureWeightChannel2)
						end	
					end
				elseif MR_KeyMotion.pull then	
					self:AnalizeAndPullChanelVec(moho, point.fAnimPos)
					self:AnalizeAndPullChanelVal(moho, point.fWidth)
					for c = 0, point:CountCurves() - 1 do
						local myCurve, where = point:Curve(c, -1)
						local curvatureChannel = myCurve:Curvature(where)
						self:AnalizeAndPullChanelVal(moho, curvatureChannel)
						if AE_Utilities then
							local curvatureOffsetChannel1 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, false)
							local curvatureOffsetChannel2 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, true)
							local curvatureWeightChannel1 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, false)
							local curvatureWeightChannel2 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, true)
							self:AnalizeAndPullChanelVal(moho, curvatureOffsetChannel1)
							self:AnalizeAndPullChanelVal(moho, curvatureOffsetChannel2)
							self:AnalizeAndPullChanelVal(moho, curvatureWeightChannel1)
							self:AnalizeAndPullChanelVal(moho, curvatureWeightChannel2)
						end	
					end
				elseif MR_KeyMotion.scale then
					if point.fAnimPos:AreDimensionsSplit() then
						local pointTranslationChannel1 = point.fAnimPos:DimensionChannel(0)
						local pointTranslationChannel2 = point.fAnimPos:DimensionChannel(1)
						self:ScaleChannel(moho, pointTranslationChannel1, false, false)
						self:ScaleChannel(moho, pointTranslationChannel2, false, false)
					else
						local pointTranslationChannel = point.fAnimPos
						self:ScaleChannel(moho, pointTranslationChannel, true, false)
					end
					self:ScaleChannel(moho, point.fWidth, false, false)
					for c = 0, point:CountCurves() - 1 do
						local myCurve, where = point:Curve(c, -1)
						local curvatureChannel = myCurve:Curvature(where)
						local curvatureOffsetChannel1 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, false)
						local curvatureOffsetChannel2 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, true)
						local curvatureWeightChannel1 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, false)
						local curvatureWeightChannel2 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, true)
						self:ScaleChannel(moho, curvatureChannel)
						self:ScaleChannel(moho, curvatureOffsetChannel1, false, false)
						self:ScaleChannel(moho, curvatureOffsetChannel2, false, false)
						self:ScaleChannel(moho, curvatureWeightChannel1, false, false)
						self:ScaleChannel(moho, curvatureWeightChannel2, false, false)
					end
				elseif MR_KeyMotion.flip then
					if point.fAnimPos:AreDimensionsSplit() then
						local pointTranslationChannel1 = point.fAnimPos:DimensionChannel(0)
						local pointTranslationChannel2 = point.fAnimPos:DimensionChannel(1)
						self:ScaleChannel(moho, pointTranslationChannel1, false, true)
						self:ScaleChannel(moho, pointTranslationChannel2, false, true)
					else
						local pointTranslationChannel = point.fAnimPos
						self:ScaleChannel(moho, pointTranslationChannel, true, true)
					end
					self:ScaleChannel(moho, point.fWidth, false, true)
					for c = 0, point:CountCurves() - 1 do
						local myCurve, where = point:Curve(c, -1)
						local curvatureChannel = myCurve:Curvature(where)
						local curvatureOffsetChannel1 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, false)
						local curvatureOffsetChannel2 = AE_Utilities:GetOffsetChannel(moho, moho.layer, myCurve, where, true)
						local curvatureWeightChannel1 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, false)
						local curvatureWeightChannel2 = AE_Utilities:GetWeightChannel(moho, moho.layer, myCurve, where, true)
						self:ScaleChannel(moho, curvatureChannel)
						self:ScaleChannel(moho, curvatureOffsetChannel1, false, true)
						self:ScaleChannel(moho, curvatureOffsetChannel2, false, true)
						self:ScaleChannel(moho, curvatureWeightChannel1, false, true)
						self:ScaleChannel(moho, curvatureWeightChannel2, false, true)
					end
				end	
			end	
		end
	end
	
	local layer = moho.layer
	self:TransformChannelVal(moho, layer.fAlpha)
	self:TransformChannelVal(moho, layer.fBlur)
	self:TransformChannelVal(moho, layer.fFollowing)
	self:TransformChannelVec(moho, layer.fTranslation)
	self:TransformChannelVal(moho, layer.fRotationX)
	self:TransformChannelVal(moho, layer.fRotationY)
	self:TransformChannelVal(moho, layer.fRotationZ)
	self:TransformChannelVec(moho, layer.fScale)
	self:TransformChannelVec(moho, layer.fShear)
	self:TransformChannelVec(moho, moho.document.fCameraTrack)
	self:TransformChannelVec(moho, moho.document.fCameraPanTilt)
	self:TransformChannelVal(moho, moho.document.fCameraZoom)
	self:TransformChannelVal(moho, moho.document.fCameraRoll)
	
	if not self.isKeysSelected then
		local dlog = MR_KeyMotionDialog:new(moho)
		if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
			return
		end
	end
	
	moho:UpdateSelectedChannels()
	moho.layer:UpdateCurFrame()
	moho:UpdateUI()
end

function MR_KeyMotion:ScaleChannel(moho, channel, isSubChannels, flip)
	local maxVal1 = -10000000
	local minVal1 = 10000000
	local maxVal2 = -10000000
	local minVal2 = 10000000
	local maxVal3 = -10000000
	local minVal3 = 10000000
	local scaleCenter1 = 0
	local scaleCenter2 = 0
	local scaleCenter3 = 0
	local vec3 = false
	if isSubChannels then
		if moho:ChannelAsAnimVec3(channel) then
			vec3 = true
		end
	end
	for keyID = 0, channel:CountKeys() - 1 do
		local keyFrame = channel:GetKeyWhen(keyID)
		if (keyFrame > 0 and channel:IsKeySelectedByID(keyID)) then
			self.isKeysSelected = true
			self:PrepUndo(moho)
			if isSubChannels then
				local channelValue = channel:GetValue(keyFrame)
				if channelValue.x > maxVal1 then
					maxVal1 = channelValue.x
				end
				if channelValue.x < minVal1 then
					minVal1 = channelValue.x
				end

				if channelValue.y > maxVal2 then
					maxVal2 = channelValue.y
				end
				if channelValue.y < minVal2 then
					minVal2 = channelValue.y
				end
				if vec3 then
					if channelValue.z > maxVal3 then
						maxVal3 = channelValue.z
					end
					if channelValue.z < minVal3 then
						minVal3 = channelValue.z
					end
				end
			else
				local channelValue = channel:GetValue(keyFrame)
				if channelValue > maxVal1 then
					maxVal1 = channelValue
				end
				if channelValue < minVal1 then
					minVal1 = channelValue
				end
			end
		end
	end
	
	if isSubChannels then
		if MR_KeyMotion.centerTop then
			scaleCenter1 = maxVal1
			scaleCenter2 = maxVal2
			if vec3 then
				scaleCenter3 = maxVal3
			end
		elseif MR_KeyMotion.centerMid then
			scaleCenter1 = maxVal1 - ((maxVal1 - minVal1) / 2)
			scaleCenter2 = maxVal2 - ((maxVal2 - minVal2) / 2)
			if vec3 then
				scaleCenter3 = maxVal3 - ((maxVal3 - minVal3) / 2)
			end
		elseif MR_KeyMotion.centerDown then
			scaleCenter1 = minVal1
			scaleCenter2 = minVal2
			if vec3 then
				scaleCenter3 = minVal3
			end
		end
	else
		if MR_KeyMotion.centerTop then
			scaleCenter1 = maxVal1
		elseif MR_KeyMotion.centerMid then
			scaleCenter1 = maxVal1 - ((maxVal1 - minVal1) / 2)
		elseif MR_KeyMotion.centerDown then
			scaleCenter1 = minVal1
		end
	end
	
	for keyID = 0, channel:CountKeys() - 1 do
		local keyFrame = channel:GetKeyWhen(keyID)
		if (keyFrame > 0 and channel:IsKeySelectedByID(keyID)) then
			self.isKeysSelected = true
			self:PrepUndo(moho)
			local channelValue = channel:GetValue(keyFrame)
			local scaleValue = MR_KeyMotion.scaleValue
			scaleValue = 1 - (scaleValue / 100)
			if MR_KeyMotion.scaleOut then
				scaleValue = 1 + (1 - scaleValue)
			end
			if isSubChannels then
				if not MR_KeyMotion.useChannelX and not MR_KeyMotion.useChannelY and not MR_KeyMotion.useChannelZ then
					return
				end
				local dif1 = channelValue.x - scaleCenter1
				local dif2 = channelValue.y - scaleCenter2
				local dif3
				if vec3 then
					dif3 = channelValue.z - scaleCenter3
				end	

				local newValue1
				local newValue2
				local newValue3
				
				if flip then
					newValue1 = scaleCenter1 - dif1
					newValue2 = scaleCenter2 - dif2
					if vec3 then
						newValue3 = scaleCenter3 - dif3
					end	
				else
					newValue1 = (dif1 * scaleValue) + scaleCenter1
					newValue2 = (dif2 * scaleValue) + scaleCenter2
					if vec3 then
						newValue3 = (dif3 * scaleValue) + scaleCenter3
					end	
				end
				
				if vec3 then
					local newVec = LM.Vector3:new_local()
					if MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY and MR_KeyMotion.useChannelZ then -- X Y Z
						newVec:Set(newValue1, newValue2, newValue3)
					elseif MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY and not MR_KeyMotion.useChannelZ then -- X Y
						newVec:Set(newValue1, newValue2, channelValue.z)	
					elseif MR_KeyMotion.useChannelX and not MR_KeyMotion.useChannelY and MR_KeyMotion.useChannelZ then -- X Z
						newVec:Set(newValue1, channelValue.y, newValue3)
					elseif MR_KeyMotion.useChannelX and not MR_KeyMotion.useChannelY and not MR_KeyMotion.useChannelZ then -- X
						newVec:Set(newValue1, channelValue.y, channelValue.z)
					elseif not MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY and MR_KeyMotion.useChannelZ then -- Y Z
						newVec:Set(channelValue.x, newValue2, newValue3)
					elseif not MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY and not MR_KeyMotion.useChannelZ then -- Y
						newVec:Set(channelValue.x, newValue2, channelValue.z)
					elseif not MR_KeyMotion.useChannelX and not MR_KeyMotion.useChannelY and MR_KeyMotion.useChannelZ then -- Z
						newVec:Set(channelValue.x, channelValue.y, newValue3)		
					end	
					channel:SetValue(keyFrame, newVec)
				else
					local newVec = LM.Vector2:new_local()
					if MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY then
						newVec:Set(newValue1, newValue2)
					elseif MR_KeyMotion.useChannelX and not MR_KeyMotion.useChannelY then
						newVec:Set(newValue1, channelValue.y)
					elseif not MR_KeyMotion.useChannelX and MR_KeyMotion.useChannelY then	
						newVec:Set(channelValue.x, newValue2)
					end
					if MR_KeyMotion.useChannelX or MR_KeyMotion.useChannelY then
						channel:SetValue(keyFrame, newVec)
					end	
				end	
			else	
				local dif1 = channelValue - scaleCenter1
				local newValue1
				if flip then
					newValue1 = scaleCenter1 - dif1
				else
					newValue1 = (dif1 * scaleValue) + scaleCenter1
				end	
				channel:SetValue(keyFrame, newValue1)
			end
		end
	end
end

function MR_KeyMotion:SmoothChannel(moho, channel, listLeft, listRight)
	local boneChannel = channel
	for i, a in pairs(listLeft) do
		local isOk = false
		local dubFound = false
		
		for k, b in pairs(listRight) do
			if a.frame == b.frame then
				if math.abs(a.difValue) > math.abs(b.difValue) then
					isOk = true
				end
				dubFound = true
			end
		end
		if not dubFound then
			isOk = true
		end
		if isOk then
			boneChannel:SetValue(a.extraFrame, boneChannel:GetValue(a.extraFrame))
			boneChannel:SetValue(a.frame, a.keyValue)
		end
	end
	for n, r in pairs(listRight) do
		local isOk = false
		local dubFound = false
		for k, b in pairs(listLeft) do
			if r.frame == b.frame then
				if math.abs(r.difValue) > math.abs(b.difValue) then
					isOk = true
				end
				dubFound = true
			end
		end
		if not dubFound then
			isOk = true
		end
		if isOk then
			boneChannel:SetValue(r.extraFrame, boneChannel:GetValue(r.extraFrame))
			boneChannel:SetValue(r.frame, r.keyValue)
		end	
	end
end

function MR_KeyMotion:AnalizeAndSmoothChanelVal(moho, channel)
	local keysAngleChangesLeft = {}
	local keysAngleChangesRight = {}
	self:AnalizeChannel(moho, channel, keysAngleChangesLeft, keysAngleChangesRight)
	self:SmoothChannel(moho, channel, keysAngleChangesLeft, keysAngleChangesRight)
end

function MR_KeyMotion:AnalizeAndSmoothChanelVec(moho, channel)
	local vec3 = false
	if channel:ChannelType() == MOHO.CHANNEL_VEC3 then
		vec3 = true
	end
	local keysPosChangesLeft = {}
	local keysPosChangesRight = {}
	local keysPosChangesLeftX = {}
	local keysPosChangesRightX = {}
	local keysPosChangesLeftY = {}
	local keysPosChangesRightY = {}
	local keysPosChangesLeftZ = {}
	local keysPosChangesRightZ = {}
	local boneChannelX = nil
	local boneChannelY = nil
	local boneChannelZ = nil
	if channel:AreDimensionsSplit() then
		if vec3 then
			boneChannelX = channel:DimensionChannel(0)
			boneChannelY = channel:DimensionChannel(1)
			boneChannelZ = channel:DimensionChannel(2)
			self:AnalizeChannel(moho, boneChannelX, keysPosChangesLeftX, keysPosChangesRightX)
			self:AnalizeChannel(moho, boneChannelY, keysPosChangesLeftY, keysPosChangesRightY)
			self:AnalizeChannel(moho, boneChannelZ, keysPosChangesLeftZ, keysPosChangesRightZ)
		else
			boneChannelX = channel:DimensionChannel(0)
			boneChannelY = channel:DimensionChannel(1)
			self:AnalizeChannel(moho, boneChannelX, keysPosChangesLeftX, keysPosChangesRightX)
			self:AnalizeChannel(moho, boneChannelY, keysPosChangesLeftY, keysPosChangesRightY)
		end	
	else
		for keyID = 0, channel:CountKeys() - 1 do
			local keyFrame = channel:GetKeyWhen(keyID)
			if (keyFrame > 0 and channel:IsKeySelectedByID(keyID)) then
				self.isKeysSelected = true
				self:PrepUndo(moho)
				-- LEFT
				if keyFrame - self.smoothFrameOffset > 0 and channel:CountKeys() > keyID + 1 then
					local isOk = false
					local targetFrame = keyFrame - self.smoothFrameOffset
					local closestKey = channel:GetKeyWhen(keyID - 1)
					local nextFrame = channel:GetKeyWhen(keyID + 1)
					if closestKey > 0 and closestKey > targetFrame then
						targetFrame = closestKey
					end
					local nextKeyValue
					local targetKeyValue
					local prevKeyValue
					
					if vec3 then
						nextKeyValue = LM.Vector3:new_local()
						targetKeyValue = LM.Vector3:new_local()
						prevKeyValue = LM.Vector3:new_local()
					else
						nextKeyValue = LM.Vector2:new_local()
						targetKeyValue = LM.Vector2:new_local()
						prevKeyValue = LM.Vector2:new_local()
					end
					targetKeyValue = channel:GetValue(keyFrame)
					nextKeyValue:Set(channel:GetValue(channel:GetKeyWhen(keyID + 1)))
					prevKeyValue = channel:GetValue(channel:GetKeyWhen(keyID - 1))
					if not channel:HasKey(targetFrame)	then
						if vec3 then
							local dif = LM.Vector3:new_local()
							dif:Set(channel:GetValue(closestKey) - targetKeyValue)
							if math.abs(self:Round(dif.x, 4)) < self.allowDifference or math.abs(self:Round(dif.y, 4)) < self.allowDifference or math.abs(self:Round(dif.z, 4)) < self.allowDifference then
								isOk = true
							end
						else
							local dif = LM.Vector2:new_local()
							dif:Set(channel:GetValue(closestKey) - targetKeyValue)
							if math.abs(self:Round(dif.x, 4)) < self.allowDifference or math.abs(self:Round(dif.y, 4)) < self.allowDifference then
								isOk = true
							end
						end	
					else
						isOk = true
					end
					if targetKeyValue ~= nextKeyValue and nextKeyValue ~= nil and isOk then
						local difValue = LM.Vector2:new_local()
						local newValue = LM.Vector2:new_local()
						if vec3 then
							difValue = LM.Vector3:new_local()
							newValue = LM.Vector3:new_local()
						end
						local newValueX
						local newValueY
						local newValueZ
						
						difValue:Set(nextKeyValue - targetKeyValue)
						if vec3 then
							newValue:Set(targetKeyValue.x + ((difValue.x / 100) * self.smoothStepValue), targetKeyValue.y + ((difValue.y / 100) * self.smoothStepValue),
							targetKeyValue.z + ((difValue.z / 100) * self.smoothStepValue))
							
							if self.useChannelX and self.useChannelY and self.useChannelZ then -- X Y Z
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
								newValueZ = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, closestKey, nextFrame)
							elseif self.useChannelX and self.useChannelY and not self.useChannelZ then -- X Y
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
								newValueZ = targetKeyValue.z
							elseif not self.useChannelX and self.useChannelY and not self.useChannelZ then -- Y
								newValueX = targetKeyValue.x
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
								newValueZ = targetKeyValue.z
							elseif not self.useChannelX and self.useChannelY and self.useChannelZ then -- Y Z
								newValueX = targetKeyValue.x
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
								newValueZ = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, closestKey, nextFrame)
							elseif self.useChannelX and not self.useChannelY and not self.useChannelZ then -- X
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = targetKeyValue.y
								newValueZ = targetKeyValue.z
							elseif self.useChannelX and not self.useChannelY and self.useChannelZ then -- X Z
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = targetKeyValue.y
								newValueZ = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, closestKey, nextFrame)	
							elseif not self.useChannelX and not self.useChannelY and self.useChannelZ then -- Z
								newValueX = targetKeyValue.x
								newValueY = targetKeyValue.y
								newValueZ = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, closestKey, nextFrame)
							end
							newValue:Set(newValueX, newValueY, newValueZ)
						else
							newValue:Set(targetKeyValue.x + ((difValue.x / 100) * self.smoothStepValue), targetKeyValue.y + ((difValue.y / 100) * self.smoothStepValue))
							
							if self.useChannelX and self.useChannelY then -- X Y Z
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
							elseif self.useChannelX and self.useChannelY and not self.useChannelZ then -- X Y
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
							elseif not self.useChannelX and self.useChannelY and not self.useChannelZ then -- Y
								newValueX = targetKeyValue.x
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
							elseif not self.useChannelX and self.useChannelY and self.useChannelZ then -- Y Z
								newValueX = targetKeyValue.x
								newValueY = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, closestKey, nextFrame)
							elseif self.useChannelX and not self.useChannelY and not self.useChannelZ then -- X
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = targetKeyValue.y
							elseif self.useChannelX and not self.useChannelY and self.useChannelZ then -- X Z
								newValueX = self:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, closestKey, nextFrame)
								newValueY = targetKeyValue.y
							elseif not self.useChannelX and not self.useChannelY and self.useChannelZ then -- Z
								newValueX = targetKeyValue.x
								newValueY = targetKeyValue.y
							end
							if self.useChannelX or self.useChannelY then
								newValue:Set(newValueX, newValueY)
							end	
						end
						
						local keyData = {}
						keyData.frame = keyFrame
						keyData.extraFrame = targetFrame
						keyData.keyValue = newValue
						keyData.difValue = difValue
						table.insert(keysPosChangesLeft, keyData)
					end	
				end
				-- RIGHT
				local isOk = false
				local targetFrame = keyFrame + self.smoothFrameOffset
				local closestKey = keyFrame
				local prewFrame = channel:GetKeyWhen(keyID - 1)
				
				local prevKeyValue
				local targetKeyValue
				local nextKeyValue
				if vec3 then
					prevKeyValue = LM.Vector3:new_local()
					targetKeyValue = LM.Vector3:new_local()
					nextKeyValue = LM.Vector3:new_local()
				else
					prevKeyValue = LM.Vector2:new_local()
					targetKeyValue = LM.Vector2:new_local()
					nextKeyValue = LM.Vector2:new_local()
				end
				prevKeyValue:Set(channel:GetValue(channel:GetKeyWhen(keyID - 1)))
				targetKeyValue:Set(channel:GetValue(keyFrame))
				
				nextKeyValue:Set(channel:GetValue(keyFrame))
				if channel:CountKeys() > keyID + 1 then
					closestKey = channel:GetKeyWhen(keyID + 1)
					nextKeyValue = channel:GetValue(channel:GetKeyWhen(keyID + 1))
				end
				
				if closestKey ~= keyFrame and closestKey < targetFrame then
					targetFrame = closestKey
				end
				
				if not channel:HasKey(targetFrame)	then
					if vec3 then
						local dif = LM.Vector3:new_local()
						dif:Set(channel:GetValue(closestKey) - targetKeyValue)
						if math.abs(self:Round(dif.x, 4)) < self.allowDifference or math.abs(self:Round(dif.y, 4)) < self.allowDifference or 
						math.abs(self:Round(dif.x, 4)) < self.allowDifference then
							isOk = true
						end
					else
						local dif = LM.Vector2:new_local()
						dif:Set(channel:GetValue(closestKey) - targetKeyValue)
						if math.abs(self:Round(dif.x, 4)) < self.allowDifference or math.abs(self:Round(dif.y, 4)) < self.allowDifference then
							isOk = true
						end
					end	
				else
					isOk = true
				end
				if targetKeyValue ~= prevKeyValue and prevKeyValue ~= nil and isOk then
					local difValue = prevKeyValue - targetKeyValue
					local newValue = LM.Vector2:new_local()
					local newValueX
					local newValueY
					local newValueZ
					if vec3 then
						newValue = LM.Vector3:new_local()
						newValue:Set(targetKeyValue.x + ((difValue.x / 100) * self.smoothStepValue), targetKeyValue.y + ((difValue.y / 100) * self.smoothStepValue),
						targetKeyValue.z + ((difValue.z / 100) * self.smoothStepValue))
						
						if self.useChannelX and self.useChannelY and self.useChannelZ then -- X Y Z
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
							newValueZ = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, prewFrame, closestKey)
						elseif self.useChannelX and self.useChannelY and not self.useChannelZ then -- X Y
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
							newValueZ = targetKeyValue.z
						elseif not self.useChannelX and self.useChannelY and not self.useChannelZ then -- Y
							newValueX = targetKeyValue.x
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
							newValueZ = targetKeyValue.z
						elseif not self.useChannelX and self.useChannelY and self.useChannelZ then -- Y Z
							newValueX = targetKeyValue.x
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
							newValueZ = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, prewFrame, closestKey)
						elseif self.useChannelX and not self.useChannelY and not self.useChannelZ then -- X
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = targetKeyValue.y
							newValueZ = targetKeyValue.z
						elseif self.useChannelX and not self.useChannelY and self.useChannelZ then -- X Z
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = targetKeyValue.y
							newValueZ = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, prewFrame, closestKey)	
						elseif not self.useChannelX and not self.useChannelY and self.useChannelZ then -- Z
							newValueX = targetKeyValue.x
							newValueY = targetKeyValue.y
							newValueZ = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.z, prevKeyValue.z, nextKeyValue.z, closestKey, targetFrame, prewFrame, closestKey)						
						end
						newValue:Set(newValueX, newValueY, newValueZ)
					else
						newValue:Set(targetKeyValue.x + ((difValue.x / 100) * self.smoothStepValue), targetKeyValue.y + ((difValue.y / 100) * self.smoothStepValue))
						
						if self.useChannelX and self.useChannelY and self.useChannelZ then -- X Y Z
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
						elseif self.useChannelX and self.useChannelY and not self.useChannelZ then -- X Y
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
						elseif not self.useChannelX and self.useChannelY and not self.useChannelZ then -- Y
							newValueX = targetKeyValue.x
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
						elseif not self.useChannelX and self.useChannelY and self.useChannelZ then -- Y Z
							newValueX = targetKeyValue.x
							newValueY = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.y, prevKeyValue.y, nextKeyValue.y, closestKey, targetFrame, prewFrame, closestKey)
						elseif self.useChannelX and not self.useChannelY and not self.useChannelZ then -- X
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = targetKeyValue.y
						elseif self.useChannelX and not self.useChannelY and self.useChannelZ then -- X Z
							newValueX = self:AnalizeAdjactedPosKeysRight(moho, targetKeyValue.x, prevKeyValue.x, nextKeyValue.x, closestKey, targetFrame, prewFrame, closestKey)
							newValueY = targetKeyValue.y
						elseif not self.useChannelX and not self.useChannelY and self.useChannelZ then -- Z
							newValueX = targetKeyValue.x
							newValueY = targetKeyValue.y					
						end
						if self.useChannelX or self.useChannelY then
							newValue:Set(newValueX, newValueY)
						end	
					end
					
					local keyData = {}
					keyData.frame = keyFrame
					keyData.extraFrame = targetFrame
					keyData.keyValue = newValue
					keyData.difValue = difValue
					table.insert(keysPosChangesRight, keyData)
				end	
			end
		end
	end	
	
	if channel:AreDimensionsSplit() then
		if vec3 then
			self:SmoothChannel(moho, boneChannelX, keysPosChangesLeftX, keysPosChangesRightX)
			self:SmoothChannel(moho, boneChannelY, keysPosChangesLeftY, keysPosChangesRightY)
			self:SmoothChannel(moho, boneChannelZ, keysPosChangesLeftZ, keysPosChangesRightZ)
		else
			self:SmoothChannel(moho, boneChannelX, keysPosChangesLeftX, keysPosChangesRightX)
			self:SmoothChannel(moho, boneChannelY, keysPosChangesLeftY, keysPosChangesRightY)
		end
	else
		for i, a in pairs(keysPosChangesLeft) do
			local isOkX = false
			local isOkY = false
			local isOkZ = false
			local dubFound = false
			
			for k, b in pairs(keysPosChangesRight) do
				if a.frame == b.frame then
					if math.abs(a.difValue.x) > math.abs(b.difValue.x) then
						isOkX = true
					end
					if math.abs(a.difValue.y) > math.abs(b.difValue.y) then
						isOkY = true
					end
					if vec3 then
						if math.abs(a.difValue.z) > math.abs(b.difValue.z) then
							isOkZ = true
						end
					end	
					dubFound = true
				end
			end
			if not dubFound then
				isOkX = true
				isOkY = true
				if vec3 then
					isOkZ = true
				end	
			end
			if isOkX then
				if vec3 then
					local xValue = LM.Vector3:new_local()
					xValue:Set(a.keyValue.x, channel:GetValue(a.frame).y, channel:GetValue(a.frame).z)
					channel:SetValue(a.extraFrame, channel:GetValue(a.extraFrame))
					channel:SetValue(a.frame, xValue)
				else
					if self.useChannelX or self.useChannelY then
						local xValue = LM.Vector2:new_local()
						xValue:Set(a.keyValue.x, channel:GetValue(a.frame).y)
						channel:SetValue(a.extraFrame, channel:GetValue(a.extraFrame))
						channel:SetValue(a.frame, xValue)
					end	
				end
			end
			if isOkY then
				if vec3 then
					local yValue = LM.Vector3:new_local()
					yValue:Set(channel:GetValue(a.frame).x, a.keyValue.y, channel:GetValue(a.frame).z)
					channel:SetValue(a.extraFrame, channel:GetValue(a.extraFrame))
					channel:SetValue(a.frame, yValue)
				else
					if self.useChannelX or self.useChannelY then
						local yValue = LM.Vector2:new_local()
						yValue:Set(channel:GetValue(a.frame).x, a.keyValue.y)
						channel:SetValue(a.extraFrame, channel:GetValue(a.extraFrame))
						channel:SetValue(a.frame, yValue)
					end	
				end
			end

			if isOkZ and vec3 then
				local zValue = LM.Vector3:new_local()
				zValue:Set(channel:GetValue(a.frame).x, channel:GetValue(a.frame).y, a.keyValue.z)
				channel:SetValue(a.extraFrame, channel:GetValue(a.extraFrame))
				channel:SetValue(a.frame, zValue)
			end
		end
		for n, r in pairs(keysPosChangesRight) do
			local isOkX = false
			local isOkY = false
			local isOkZ = false
			local dubFound = false
			for k, b in pairs(keysPosChangesLeft) do
				if r.frame == b.frame then
					if math.abs(r.difValue.x) > math.abs(b.difValue.x) then
						isOkX = true
					end
					if math.abs(r.difValue.y) > math.abs(b.difValue.y) then
						isOkY = true
					end
					if vec3 then
						if math.abs(r.difValue.z) > math.abs(b.difValue.z) then
							isOkZ = true
						end
					end	
					dubFound = true
				end
			end
			if not dubFound then
				isOkX = true
				isOkY = true
				if vec3 then
					isOkZ = true
				end	
			end
			if isOkX then
				if vec3 then
					local xValue = LM.Vector3:new_local()
					xValue:Set(r.keyValue.x, channel:GetValue(r.frame).y, channel:GetValue(r.frame).z)
					channel:SetValue(r.extraFrame, channel:GetValue(r.extraFrame))
					channel:SetValue(r.frame, xValue)
				else
					if self.useChannelX or self.useChannelY then
						local xValue = LM.Vector2:new_local()
						xValue:Set(r.keyValue.x, channel:GetValue(r.frame).y)
						channel:SetValue(r.extraFrame, channel:GetValue(r.extraFrame))
						channel:SetValue(r.frame, xValue)
					end	
				end	
			end	
			if isOkY then
				if vec3 then 
					local yValue = LM.Vector3:new_local()
					yValue:Set(channel:GetValue(r.frame).x, r.keyValue.y, channel:GetValue(r.frame).z)
					channel:SetValue(r.extraFrame, channel:GetValue(r.extraFrame))
					channel:SetValue(r.frame, yValue)
				else
					if self.useChannelX or self.useChannelY then
						local yValue = LM.Vector2:new_local()
						yValue:Set(channel:GetValue(r.frame).x, r.keyValue.y)
						channel:SetValue(r.extraFrame, channel:GetValue(r.extraFrame))
						channel:SetValue(r.frame, yValue)
					end	
				end	
			end

			if isOkZ and vec3 then
				local zValue = LM.Vector3:new_local()
				zValue:Set(channel:GetValue(r.frame).x, channel:GetValue(r.frame).y, r.keyValue.z)
				channel:SetValue(r.extraFrame, channel:GetValue(r.extraFrame))
				channel:SetValue(r.frame, zValue)
			end
		end	
	end
end

function MR_KeyMotion:AnalizeChannel(moho, channel, listLeft, listRight)
	local boneChannel = channel
	for keyID = 0, boneChannel:CountKeys() - 1 do
		local keyFrame = boneChannel:GetKeyWhen(keyID)
		if (keyFrame > 0 and boneChannel:IsKeySelectedByID(keyID)) then
			self.isKeysSelected = true
			self:PrepUndo(moho)
			-- LEFT
			if keyFrame - self.smoothFrameOffset > 0 and boneChannel:CountKeys() > keyID + 1 then
				local isOk = true
				local targetFrame = keyFrame - self.smoothFrameOffset
				local closestKey = boneChannel:GetKeyWhen(keyID - 1)
				local nextKey = boneChannel:GetValue(keyFrame)
				if closestKey > 0 and closestKey > targetFrame then
					targetFrame = closestKey
				end
				local targetKeyValue = boneChannel:GetValue(keyFrame)
				local nextKeyValue = targetKeyValue
				if boneChannel:CountKeys() > keyID + 1 then
					nextKeyValue = boneChannel:GetValue(boneChannel:GetKeyWhen(keyID + 1))
					nextKey = boneChannel:GetKeyWhen(keyID + 1)
				end
				
				if not boneChannel:HasKey(targetFrame)	then
					local dif = self:Round(boneChannel:GetValue(closestKey) - targetKeyValue, 4)
					if math.abs(dif) > self.allowDifference then
						isOk = false
					end
				end
				
				if targetKeyValue ~= nextKeyValue and nextKeyValue ~= nil and isOk then
					local smoothThresholdValue = self.smoothThresholdValue
					smoothThresholdValue = smoothThresholdValue / math.abs(((boneChannel:GetKeyWhen(keyID - 1) - nextKey) / 50))
					local prevKeyValue = boneChannel:GetValue(boneChannel:GetKeyWhen(keyID - 1))
					local difAdjactedPrcntValue = ((prevKeyValue - nextKeyValue) / 100) * smoothThresholdValue
					local difValue = nextKeyValue - targetKeyValue
					local newValue = targetKeyValue + ((difValue / 100) * self.smoothStepValue)
					if closestKey >= targetFrame then
						if prevKeyValue > nextKeyValue then
							if targetKeyValue < nextKeyValue then
								difValue = -difValue
							end
							if prevKeyValue - difAdjactedPrcntValue > targetKeyValue then
								newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
								if prevKeyValue - difAdjactedPrcntValue < newValue then
									local tempValue = ((difValue / 100) * self.smoothStepValue)
									local maxSteps = 10
									local steps = 0
									repeat
										if steps > maxSteps then
											newValue = prevKeyValue - difAdjactedPrcntValue
											break
										end
										tempValue = tempValue * 0.75
										steps = steps + 1
									until prevKeyValue - difAdjactedPrcntValue > targetKeyValue - tempValue
									newValue = targetKeyValue - tempValue
								end	
							end
							
							if prevKeyValue - difAdjactedPrcntValue > newValue and prevKeyValue - difAdjactedPrcntValue < targetKeyValue then
								local tempValue = ((difValue / 100) * self.smoothStepValue)
								local maxSteps = 10
								local steps = 0
								repeat 
									if steps > maxSteps then
										newValue = prevKeyValue - difAdjactedPrcntValue
										break
									end
									tempValue = tempValue * 0.75
									steps = steps + 1
								until prevKeyValue - difAdjactedPrcntValue < targetKeyValue + tempValue
								newValue = targetKeyValue + tempValue
							end
						else
							if targetKeyValue > nextKeyValue then
								difValue = -difValue
							end
							if prevKeyValue - difAdjactedPrcntValue < targetKeyValue then
								newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
								if prevKeyValue - difAdjactedPrcntValue > newValue then
									local tempValue = ((difValue / 100) * self.smoothStepValue)
									local maxSteps = 10
									local steps = 0
									repeat 
										if steps > maxSteps then
											newValue = prevKeyValue - difAdjactedPrcntValue
											break
										end
										tempValue = tempValue * 0.75
										steps = steps + 1
									until prevKeyValue - difAdjactedPrcntValue < targetKeyValue - tempValue
									newValue = targetKeyValue - tempValue
								end	
							end
							
							if prevKeyValue - difAdjactedPrcntValue < newValue and prevKeyValue - difAdjactedPrcntValue > targetKeyValue then
								local tempValue = ((difValue / 100) * self.smoothStepValue)
								local maxSteps = 10
								local steps = 0
								repeat 
									if steps > maxSteps then
										newValue = prevKeyValue - difAdjactedPrcntValue
										break
									end
									tempValue = tempValue * 0.75
									steps = steps + 1
								until prevKeyValue - difAdjactedPrcntValue > targetKeyValue + tempValue
								newValue = targetKeyValue + tempValue
							end
						end
					end
					
					local keyData = {}
					keyData.frame = keyFrame
					keyData.extraFrame = targetFrame
					keyData.keyValue = newValue
					keyData.difValue = difValue
					table.insert(listLeft, keyData)
				end	
			end
			-- RIGHT
			local isOkR = true
			local targetFrame = keyFrame + self.smoothFrameOffset
			local closestKey = keyFrame
			local nextKeyValue = boneChannel:GetValue(keyFrame)
			local nextKey = boneChannel:GetValue(keyFrame)
			if boneChannel:CountKeys() - 1 > keyID + 1 then
				closestKey = boneChannel:GetKeyWhen(keyID + 1)
				nextKeyValue = boneChannel:GetValue(boneChannel:GetKeyWhen(keyID + 1))
				nextKey = boneChannel:GetKeyWhen(keyID + 1)
			end
			if closestKey ~= keyFrame and closestKey < targetFrame then
				targetFrame = closestKey
			end
			local prevKeyValue = boneChannel:GetValue(boneChannel:GetKeyWhen(keyID - 1))
			local targetKeyValue = boneChannel:GetValue(keyFrame)
			if not boneChannel:HasKey(targetFrame)	then
				local dif = self:Round(boneChannel:GetValue(closestKey) - targetKeyValue, 4)
				if math.abs(dif) > self.allowDifference then
					isOkR = false
				end
			end
			if targetKeyValue ~= prevKeyValue and prevKeyValue ~= nil and isOkR then
				local smoothThresholdValue = self.smoothThresholdValue
				smoothThresholdValue = smoothThresholdValue / math.abs(((boneChannel:GetKeyWhen(keyID - 1) - closestKey) / 50))

				local difAdjactedPrcntValue = ((prevKeyValue - nextKeyValue) / 100) * smoothThresholdValue
				local difValue = prevKeyValue - targetKeyValue
				local newValue = targetKeyValue + ((difValue / 100) * self.smoothStepValue)
				if closestKey >= targetFrame then
					if prevKeyValue < nextKeyValue then
						if targetKeyValue < prevKeyValue then
							difValue = -difValue
						end
						if nextKeyValue + difAdjactedPrcntValue > targetKeyValue then
							newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
							if nextKeyValue + difAdjactedPrcntValue < newValue then
								local tempValue = ((difValue / 100) * self.smoothStepValue)
								local maxSteps = 10
								local steps = 0
								repeat 
									if steps > maxSteps then
										newValue = newValue + difAdjactedPrcntValue
										break
									end
									tempValue = tempValue * 0.75
									steps = steps + 1
								until nextKeyValue + difAdjactedPrcntValue > targetKeyValue - tempValue
								newValue = targetKeyValue - tempValue
							end	
						end
						
						if nextKeyValue + difAdjactedPrcntValue > newValue and nextKeyValue + difAdjactedPrcntValue < targetKeyValue then
							local tempValue = ((difValue / 100) * self.smoothStepValue)
							local maxSteps = 10
							local steps = 0
							repeat 
								if steps > maxSteps then
									newValue = newValue + difAdjactedPrcntValue
									break
								end
								tempValue = tempValue * 0.75
								steps = steps + 1
							until nextKeyValue + difAdjactedPrcntValue < targetKeyValue + tempValue
							newValue = targetKeyValue + tempValue
						end
					else
						if targetKeyValue > prevKeyValue then
							difValue = -difValue
						end
						if nextKeyValue + difAdjactedPrcntValue < targetKeyValue then
							newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
							if nextKeyValue + difAdjactedPrcntValue > newValue then
								local tempValue = ((difValue / 100) * self.smoothStepValue)
								local maxSteps = 10
								local steps = 0
								repeat 
									if steps > maxSteps then
										newValue = newValue + difAdjactedPrcntValue
										break
									end
									tempValue = tempValue * 0.75
									steps = steps + 1
								until nextKeyValue + difAdjactedPrcntValue < targetKeyValue - tempValue
								newValue = targetKeyValue - tempValue
							end	
						end
						
						if nextKeyValue + difAdjactedPrcntValue < newValue and nextKeyValue + difAdjactedPrcntValue > targetKeyValue then
							local tempValue = ((difValue / 100) * self.smoothStepValue)
							local maxSteps = 10
							local steps = 0
							repeat 
								if steps > maxSteps then
									newValue = newValue + difAdjactedPrcntValue
									break
								end
								tempValue = tempValue * 0.75
								steps = steps + 1
							until nextKeyValue + difAdjactedPrcntValue > targetKeyValue + tempValue
							newValue = targetKeyValue + tempValue
						end
					end
				end
				local keyData = {}
				keyData.frame = keyFrame
				keyData.extraFrame = targetFrame
				keyData.keyValue = newValue
				keyData.difValue = difValue
				table.insert(listRight, keyData)
			end	
		end
	end	
end

function MR_KeyMotion:AnalizeAdjactedPosKeysLeft(moho, targetKeyValue, prevKeyValue, nextKeyValue, closestKey, targetFrame, prewFrame, nextFreme)
	local smoothThresholdValue = self.smoothThresholdValue
	smoothThresholdValue = smoothThresholdValue / math.abs(((prewFrame - nextFreme) / 50))
	local difAdjactedPrcntValue = ((prevKeyValue - nextKeyValue) / 100) * smoothThresholdValue
	local difValue = nextKeyValue - targetKeyValue
	local newValue = targetKeyValue + ((difValue / 100) * self.smoothStepValue)
	if closestKey >= targetFrame then
		if prevKeyValue > nextKeyValue then
			if targetKeyValue < nextKeyValue then
				difValue = -difValue
			end
			if prevKeyValue - difAdjactedPrcntValue > targetKeyValue then
				newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
				if prevKeyValue - difAdjactedPrcntValue < newValue then
					local tempValue = ((difValue / 100) * self.smoothStepValue)
					local maxSteps = 10
					local steps = 0
					repeat 
						if steps > maxSteps then
							newValue = prevKeyValue - difAdjactedPrcntValue
							break
						end
						tempValue = tempValue * 0.75
						steps = steps + 1
					until prevKeyValue - difAdjactedPrcntValue > targetKeyValue - tempValue
					newValue = targetKeyValue - tempValue
				end	
			end
			
			if prevKeyValue - difAdjactedPrcntValue > newValue and prevKeyValue - difAdjactedPrcntValue < targetKeyValue then
				local tempValue = ((difValue / 100) * self.smoothStepValue)
				local maxSteps = 10
				local steps = 0
				repeat 
					if steps > maxSteps then
						newValue = prevKeyValue - difAdjactedPrcntValue
						break
					end
					tempValue = tempValue * 0.75
					steps = steps + 1
				until prevKeyValue - difAdjactedPrcntValue < targetKeyValue + tempValue
				newValue = targetKeyValue + tempValue
			end
		else
			if targetKeyValue > nextKeyValue then
				difValue = -difValue
			end
			if prevKeyValue - difAdjactedPrcntValue < targetKeyValue then
				newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
				if prevKeyValue - difAdjactedPrcntValue > newValue then
					local tempValue = ((difValue / 100) * self.smoothStepValue)
					local maxSteps = 10
					local steps = 0
					repeat 
						if steps > maxSteps then
							newValue = prevKeyValue - difAdjactedPrcntValue
							break
						end
						tempValue = tempValue * 0.75
						steps = steps + 1
					until prevKeyValue - difAdjactedPrcntValue < targetKeyValue - tempValue
					newValue = targetKeyValue - tempValue
				end	
			end
			
			if prevKeyValue - difAdjactedPrcntValue < newValue and prevKeyValue - difAdjactedPrcntValue > targetKeyValue then
				local tempValue = ((difValue / 100) * self.smoothStepValue)
				local maxSteps = 10
				local steps = 0
				repeat 
					if steps > maxSteps then
						newValue = prevKeyValue - difAdjactedPrcntValue
						break
					end
					tempValue = tempValue * 0.75
					steps = steps + 1
				until prevKeyValue - difAdjactedPrcntValue > targetKeyValue + tempValue
				newValue = targetKeyValue + tempValue
			end
		end
	end
	return newValue
end

function MR_KeyMotion:AnalizeAdjactedPosKeysRight(moho, targetKeyValue, prevKeyValue, nextKeyValue, closestKey, targetFrame, prewFrame, nextFreme)
	local smoothThresholdValue = self.smoothThresholdValue
	smoothThresholdValue = smoothThresholdValue / math.abs(((prewFrame - nextFreme) / 50))
	local difAdjactedPrcntValue = ((prevKeyValue - nextKeyValue) / 100) * smoothThresholdValue
	local difValue = prevKeyValue - targetKeyValue
	local newValue = targetKeyValue + ((difValue / 100) * self.smoothStepValue)
	if closestKey >= targetFrame then
		if prevKeyValue < nextKeyValue then
			if targetKeyValue < prevKeyValue then
				difValue = -difValue
			end
			if nextKeyValue + difAdjactedPrcntValue > targetKeyValue then
				newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
				if nextKeyValue + difAdjactedPrcntValue < newValue then
					local tempValue = ((difValue / 100) * self.smoothStepValue)
					local maxSteps = 10
					local steps = 0
					repeat 
						if steps > maxSteps then
							newValue = nextKeyValue + difAdjactedPrcntValue
							break
						end
						tempValue = tempValue * 0.6
						steps = steps + 1
					until nextKeyValue + difAdjactedPrcntValue > targetKeyValue - tempValue
					newValue = targetKeyValue - tempValue
				end	
			end
			
			if nextKeyValue + difAdjactedPrcntValue > newValue and nextKeyValue + difAdjactedPrcntValue < targetKeyValue then
				local tempValue = ((difValue / 100) * self.smoothStepValue)
				local maxSteps = 10
				local steps = 0
				repeat 
					if steps > maxSteps then
						newValue = nextKeyValue + difAdjactedPrcntValue
						break
					end
					tempValue = tempValue * 0.6
					steps = steps + 1
				until nextKeyValue + difAdjactedPrcntValue < targetKeyValue + tempValue
				newValue = targetKeyValue + tempValue
			end
		else
			if targetKeyValue > prevKeyValue then
				difValue = -difValue
			end
			if nextKeyValue + difAdjactedPrcntValue < targetKeyValue then
				newValue = targetKeyValue - ((difValue / 100) * self.smoothStepValue)
				if nextKeyValue + difAdjactedPrcntValue > newValue then
					local tempValue = ((difValue / 100) * self.smoothStepValue)
					local maxSteps = 10
					local steps = 0
					repeat
						if steps > maxSteps then
							newValue = nextKeyValue + difAdjactedPrcntValue
							break
						end
						tempValue = tempValue * 0.6
						steps = steps + 1
					until nextKeyValue + difAdjactedPrcntValue < targetKeyValue - tempValue
					newValue = targetKeyValue - tempValue
				end	
			end
			
			if nextKeyValue + difAdjactedPrcntValue < newValue and nextKeyValue + difAdjactedPrcntValue > targetKeyValue then
				local tempValue = ((difValue / 100) * self.smoothStepValue)
				local maxSteps = 10
				local steps = 0
				repeat
					if steps > maxSteps then
						newValue = nextKeyValue + difAdjactedPrcntValue
						break
					end
					tempValue = tempValue * 0.6
					steps = steps + 1
				until nextKeyValue + difAdjactedPrcntValue > targetKeyValue + tempValue
				newValue = targetKeyValue + tempValue
			end
		end
	end
	return newValue
end

function MR_KeyMotion:AnalizeAndPullChanelVal(moho, channel)
	local keysAngleChanges = {}
	self:AnalizeChannelForPull(moho, channel, keysAngleChanges)
	self:PullChannel(moho, channel, keysAngleChanges)
end

function MR_KeyMotion:AnalizeAndPullChanelVec(moho, channel)
	local keysPosChanges = {}
	local keysPosChangesX = {}
	local keysPosChangesY = {}
	local keysPosChangesZ = {}
	local channelX
	local channelY
	local channelZ
	local vec3 = false
	if moho:ChannelAsAnimVec3(channel) then
		vec3 = true
	end
	if channel:AreDimensionsSplit() then
		channelX = channel:DimensionChannel(0)
		channelY = channel:DimensionChannel(1)
		self:AnalizeChannelForPull(moho, channelX, keysPosChangesX)
		self:AnalizeChannelForPull(moho, channelY, keysPosChangesY)
		if vec3 then
			channelZ = channel:DimensionChannel(2)
			self:AnalizeChannelForPull(moho, channelZ, keysPosChangesZ)
		end	
	else
		for keyID = 0, channel:CountKeys() - 1 do
			local keyFrame = channel:GetKeyWhen(keyID)
			if (keyFrame - self.pullFrameOffset > 0 and channel:IsKeySelectedByID(keyID)) then
				self.isKeysSelected = true
				self:PrepUndo(moho)
				local keyData = {}
				keyData.frame = keyFrame
				table.insert(keysPosChanges, keyData)
			end
		end
	end	
	
	if channel:AreDimensionsSplit() then
		self:PullChannel(moho, channelX, keysPosChangesX)
		self:PullChannel(moho, channelY, keysPosChangesY)
		if vec3 then
			self:PullChannel(moho, channelZ, keysPosChangesZ)
		end	
	else
		for i, a in pairs(keysPosChanges) do
			local prevKey = a.frame - self.pullFrameOffset
			local nextKey = a.frame + self.pullFrameOffset
			local closestPrevKey = channel:GetKeyWhen(channel:GetClosestKeyID(a.frame - 1))
			local closestNextKey = nextKey
			if channel:CountKeys() > channel:GetClosestKeyID(a.frame) + 1 then
				closestNextKey = channel:GetKeyWhen(channel:GetClosestKeyID(a.frame) + 1)
			end
			if closestPrevKey < a.frame and closestPrevKey > prevKey then
				prevKey = closestPrevKey
			end
			if closestNextKey > a.frame and closestNextKey < nextKey then
				nextKey = closestNextKey
			end
			local targetKeyVal = LM.Vector2:new_local()
			targetKeyVal:Set(channel:GetValue(a.frame))
			
			local val1
			local dif1
			local newValue1
			local val2
			local dif2
			local newValue2
			local dif3
			local newValue3
				
			if vec3 then
				targetKeyVal = LM.Vector3:new_local()
				targetKeyVal:Set(channel:GetValue(a.frame))
				val1 = LM.Vector3:new_local()
				dif1 = LM.Vector3:new_local()
				newValue1 = LM.Vector3:new_local()
				val1:Set(channel:GetValue(prevKey))
				dif1:Set(val1 - targetKeyVal)
				newValue1:Set(val1.x - (dif1.x / 100) * self.pullStepValue, val1.y - (dif1.y / 100) * self.pullStepValue, val1.z - (dif1.z / 100) * self.pullStepValue)
				
				val2 = LM.Vector3:new_local()
				dif2 = LM.Vector3:new_local()
				newValue2 = LM.Vector3:new_local()
				val2:Set(channel:GetValue(nextKey))
				dif2:Set(val2 - targetKeyVal)
				newValue2:Set(val2.x - (dif2.x / 100) * self.pullStepValue, val2.y - (dif2.y / 100) * self.pullStepValue, val2.z - (dif2.z / 100) * self.pullStepValue)
			else
				val1 = LM.Vector2:new_local()
				dif1 = LM.Vector2:new_local()
				newValue1 = LM.Vector2:new_local()
				val1:Set(channel:GetValue(prevKey))
				dif1:Set(val1 - targetKeyVal)
				newValue1:Set(val1.x - (dif1.x / 100) * self.pullStepValue, val1.y - (dif1.y / 100) * self.pullStepValue)
				
				val2 = LM.Vector2:new_local()
				dif2 = LM.Vector2:new_local()
				newValue2 = LM.Vector2:new_local()
				val2:Set(channel:GetValue(nextKey))
				dif2:Set(val2 - targetKeyVal)
				newValue2:Set(val2.x - (dif2.x / 100) * self.pullStepValue, val2.y - (dif2.y / 100) * self.pullStepValue)
			end
			
			if vec3 then
				if val1.x > targetKeyVal.x and val2.x > targetKeyVal.x or val1.x < targetKeyVal.x and val2.x < targetKeyVal.x then
					local newValueX1 = LM.Vector3:new_local()
					local newValueX2 = LM.Vector3:new_local()
					if self.useChannelX then
						newValueX1:Set(newValue1.x, val1.y, val1.z)
						newValueX2:Set(newValue2.x, val2.y, val1.z)
					else
						newValueX1:Set(val1.x, val1.y, val1.z)
						newValueX2:Set(val1.x, val2.y, val1.z)
					end
					channel:SetValue(prevKey, newValueX1)
					channel:SetValue(nextKey, newValueX2)
				end
				if val1.y > targetKeyVal.y and val2.y > targetKeyVal.y or val1.y < targetKeyVal.y and val2.y < targetKeyVal.y then
					val1:Set(channel:GetValue(prevKey))
					val2:Set(channel:GetValue(nextKey))
					local newValueY1 = LM.Vector3:new_local()
					local newValueY2 = LM.Vector3:new_local()
					if self.useChannelY then
						newValueY1:Set(val1.x, newValue1.y, val1.z)
						newValueY2:Set(val2.x, newValue2.y, val1.z)
					else
						newValueY1:Set(val1.x, val1.y, val1.z)
						newValueY2:Set(val2.x, val1.y, val1.z)
					end
					channel:SetValue(prevKey, newValueY1)
					channel:SetValue(nextKey, newValueY2)
				end	
				if val1.z > targetKeyVal.z and val2.z > targetKeyVal.z or val1.z < targetKeyVal.z and val2.z < targetKeyVal.z then
					val1:Set(channel:GetValue(prevKey))
					val2:Set(channel:GetValue(nextKey))
					local newValueY1 = LM.Vector3:new_local()
					local newValueY2 = LM.Vector3:new_local()
					if self.useChannelZ then
						newValueY1:Set(val1.x, val1.y, newValue1.z)
						newValueY2:Set(val2.x, val1.y, newValue2.z)
					else
						newValueY1:Set(val1.x, val1.y, val1.z)
						newValueY2:Set(val2.x, val1.y, val1.z)
					end
					channel:SetValue(prevKey, newValueY1)
					channel:SetValue(nextKey, newValueY2)
				end	
			else
				if val1.x > targetKeyVal.x and val2.x > targetKeyVal.x or val1.x < targetKeyVal.x and val2.x < targetKeyVal.x then
					local newValueX1 = LM.Vector2:new_local()
					local newValueX2 = LM.Vector2:new_local()
					if self.useChannelX then
						newValueX1:Set(newValue1.x, val1.y)
						newValueX2:Set(newValue2.x, val2.y)
					else
						newValueX1:Set(val1.x, val1.y)
						newValueX2:Set(val1.x, val2.y)
					end
					if self.useChannelX or self.useChannelY then
						channel:SetValue(prevKey, newValueX1)
						channel:SetValue(nextKey, newValueX2)
					end	
				end
				if val1.y > targetKeyVal.y and val2.y > targetKeyVal.y or val1.y < targetKeyVal.y and val2.y < targetKeyVal.y then
					val1:Set(channel:GetValue(prevKey))
					val2:Set(channel:GetValue(nextKey))
					local newValueY1 = LM.Vector2:new_local()
					local newValueY2 = LM.Vector2:new_local()
					if self.useChannelY then
						newValueY1:Set(val1.x, newValue1.y)
						newValueY2:Set(val2.x, newValue2.y)
					else
						newValueY1:Set(val1.x, val1.y)
						newValueY2:Set(val2.x, val1.y)
					end
					if self.useChannelX or self.useChannelY then
						channel:SetValue(prevKey, newValueY1)
						channel:SetValue(nextKey, newValueY2)
					end	
				end
			end	
		end
	end
end

function MR_KeyMotion:AnalizeChannelForPull(moho, channel, keyList)
	local boneChannel = channel
	for keyID = 0, boneChannel:CountKeys() - 1 do
		local keyFrame = boneChannel:GetKeyWhen(keyID)
		if (keyFrame - self.pullFrameOffset > 0 and boneChannel:IsKeySelectedByID(keyID)) then
			self.isKeysSelected = true
			self:PrepUndo(moho)
			local keyData = {}
			keyData.frame = keyFrame
			table.insert(keyList, keyData)
		end
	end	
end

function MR_KeyMotion:PullChannel(moho, channel, keyList)
	for i, a in pairs(keyList) do
		local val1 = channel:GetValue(a.frame - self.pullFrameOffset)
		local dif1 = val1 - channel:GetValue(a.frame) 
		local newValue1 = val1 - (dif1 / 100) * self.pullStepValue
		channel:SetValue(a.frame - self.pullFrameOffset, newValue1)
		
		local val2 = channel:GetValue(a.frame + self.pullFrameOffset)
		local dif2 = val2 - channel:GetValue(a.frame) 
		local newValue2 = val2 - (dif2 / 100) * self.pullStepValue
		channel:SetValue(a.frame + self.pullFrameOffset, newValue2)
	end
end

function MR_KeyMotion:Round(x, n)
	if self.mohoVersion >= 14 then
		n = 10 ^ (n or 3)
	else	
		n = math.pow(10, n or 3)
	end	
	x = x * n
	if x >= 0 then x = math.floor(x + 0.5) else x = math.ceil(x - 0.5) end
	return x / n
end

function MR_KeyMotion:PrepUndo(moho)
	if not self.isUndoPrepared then
		moho.document:SetDirty()
		moho.document:PrepUndo(moho.layer, true)
		self.isUndoPrepared = true
	end	
end

function MR_KeyMotion:TransformChannelVal(moho, channel)
	for keyID = 0, channel:CountKeys() - 1 do
		local keyFrame = channel:GetKeyWhen(keyID)
		if (keyFrame > 0 and channel:IsKeySelectedByID(keyID)) then
			self.isKeysSelected = true
			if MR_KeyMotion.smooth then
				self:AnalizeAndSmoothChanelVal(moho, channel)
			elseif MR_KeyMotion.pull then	
				self:AnalizeAndPullChanelVal(moho, channel)
			elseif MR_KeyMotion.scale then
				self:ScaleChannel(moho, channel, false, false)
			end
			break
		end	
	end
end
function MR_KeyMotion:TransformChannelVec(moho, channel)
	if channel:AreDimensionsSplit() then
		for i=0, 2 do
			local subChannel = channel:DimensionChannel(i)
			for keyID = 0, subChannel:CountKeys() - 1 do
				local keyFrame = subChannel:GetKeyWhen(keyID)
				if (keyFrame > 0 and subChannel:IsKeySelectedByID(keyID)) then
					self.isKeysSelected = true
					if MR_KeyMotion.smooth then
						self:AnalizeAndSmoothChanelVec(moho, channel)
					elseif MR_KeyMotion.pull then	
						self:AnalizeAndPullChanelVec(moho, channel)
					elseif MR_KeyMotion.scale then
						self:ScaleChannel(moho, subChannel, false, false)
					elseif MR_KeyMotion.flip then
						self:ScaleChannel(moho, subChannel, false, true)	
					end
					break
				end
			end
		end
	else
		for keyID = 0, channel:CountKeys() - 1 do
			local keyFrame = channel:GetKeyWhen(keyID)
			if (keyFrame > 0 and channel:IsKeySelectedByID(keyID)) then
				self.isKeysSelected = true
				if MR_KeyMotion.smooth then
					self:AnalizeAndSmoothChanelVec(moho, channel)
				elseif MR_KeyMotion.pull then	
					self:AnalizeAndPullChanelVec(moho, channel)
				elseif MR_KeyMotion.scale then
					self:ScaleChannel(moho, channel, true, false)
				elseif MR_KeyMotion.flip then
					self:ScaleChannel(moho, channel, true, true)
				end
				break
			end	
		end
	end	
end

function MR_KeyMotion:GetMohoVersion(moho)
	local numVers = {}
	local vers = moho:AppVersion()
	for n in string.gmatch (vers, "%d+") do
		table.insert(numVers, tonumber(n))
	end
	return numVers[1], numVers[2], numVers[3]
end

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

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

	phrase['Description'] = 'Smooth, pull, scale and flip keys motion'
	phrase['UILabel'] = 'Key Motion 1.1'
	
	phrase['Smooth'] = 'Smooth'
    phrase['Pull'] = 'Pull'
    phrase['Scale'] = 'Scale'
    phrase['Flip'] = 'Flip'
	phrase['Transform Top'] = 'Transform From Top'
	phrase['Transform Mid'] = 'Transform From Center'
	phrase['Transform Down'] = 'Transform From Down'
	phrase['Scale In'] = 'Scale In'
	phrase['Scale Out'] = 'Scale Out'
	phrase['Transform X'] = 'Transform X Channel'
	phrase['Transform Y'] = 'Transform Y Channel'
	phrase['Smooth Percentage'] = ''
	phrase['Scale Percentage'] = ''
	phrase['Pull Percentage'] = ''

	return phrase[text]
end

Icon
MR Key Motion
Listed

Script type: Button/Menu

Uploaded: Feb 25 2023, 19:21

Last modified: Sep 12 2023, 11:52

This script helps you to smooth your animation in a fast and convenient way.

Installation Options:

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: 1369