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

ScriptName = "MR_TransformRigTool"

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

MR_TransformRigTool = {}

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

function MR_TransformRigTool:Version()
	return '1.0'
end

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

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

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

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

function MR_TransformRigTool:IsRelevant(moho)
	local v1, v2, v3 = self:GetMohoVersion(moho)
	self.isVitruvianBonesAvaible = true
	self.isBones1353 = true
	if not v3 then
		v3 = 0
	end
	if v1 < 13 then
		self.isVitruvianBonesAvaible = false
		self.isBones1353 = false
	elseif v2 < 5 then
		self.isVitruvianBonesAvaible = false
		self.isBones1353 = false
	elseif v3 < 3 then	
		self.isBones1353 = false
	end
	return true
end

function MR_TransformRigTool:IsEnabled(moho)
	return true
end

function MR_TransformRigTool:SupportsGPUMode(moho)
	return false
end

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

function MR_TransformRigTool:LoadPrefs(prefs)
	self.considerNewParentRotation = prefs:GetBool('MR_TransformRigTool.considerNewParentRotation', false)
	self.considerOldParentRotation = prefs:GetBool('MR_TransformRigTool.considerOldParentRotation', false)
	self.centerId = prefs:GetInt("MR_TransformRigTool.centerId", 0)
	self.selectBonesBt = prefs:GetBool("MR_TransformRigTool.selectBonesBt", true)
    self.translateBt = prefs:GetBool("MR_TransformRigTool.translateBt", false)
    self.scaleBt = prefs:GetBool("MR_TransformRigTool.scaleBt", false)
    self.rotateBt = prefs:GetBool("MR_TransformRigTool.rotateBt", false)
    self.transformBones = prefs:GetBool("MR_TransformRigTool.transformBones", false)
    self.transformPoints = prefs:GetBool("MR_TransformRigTool.transformPoints", true)
    self.autoSelect = prefs:GetBool("MR_TransformRigTool.autoSelect", false)
    self.ignoreRefLayers = prefs:GetBool("MR_TransformRigTool.ignoreRefLayers", true)
    self.transformOrigin = prefs:GetBool("MR_TransformRigTool.transformOrigin", true)
    self.transformVitruvianBones = prefs:GetBool("MR_TransformRigTool.transformVitruvianBones", true)
    self.transformTargetBones = prefs:GetBool("MR_TransformRigTool.transformTargetBones", true)
    self.adjustStrokeWidth = prefs:GetBool("MR_TransformRigTool.adjustStrokeWidth", false)
    self.transformImageLayers = prefs:GetBool("MR_TransformRigTool.transformImageLayers", false)
    self.transformInfo = prefs:GetBool("MR_TransformRigTool.transformInfo", true)
    self.followPathAdaptation = prefs:GetBool("MR_TransformRigTool.followPathAdaptation", false)
    self.transformPatchLayers = prefs:GetBool("MR_TransformRigTool.transformPatchLayers", true)
    self.useGlobalFlip = prefs:GetBool("MR_TransformRigTool.useGlobalFlip", false)
    self.adaptiveScaleAndRotation = prefs:GetBool("MR_TransformRigTool.adaptiveScaleAndRotation", false)
    self.useLineWidthInsteadStrokeWidth = prefs:GetBool("MR_TransformRigTool.useLineWidthInsteadStrokeWidth", false)
    self.liveUpdatingReferences = prefs:GetBool("MR_TransformRigTool.liveUpdatingReferences", true)
    self.duplicatedBodypartSuffix = prefs:GetString("MR_TransformRigTool.duplicatedBodypartSuffix", ' Dup')
    self.directionMode = prefs:GetBool("MR_TransformRigTool.directionMode", true)
    self.rDirection = prefs:GetBool("MR_TransformRigTool.rDirection", true)
    self.lDirection = prefs:GetBool("MR_TransformRigTool.lDirection", false)
    self.customMode = prefs:GetBool("MR_TransformRigTool.customMode", false)
    self.suffixMode = prefs:GetBool("MR_TransformRigTool.suffixMode", false)
    self.customModeInput1 = prefs:GetString("MR_TransformRigTool.customModeInput1", 'Front')
    self.customModeInput2 = prefs:GetString("MR_TransformRigTool.customModeInput2", 'Back')
    self.counterDelimiter = prefs:GetString("MR_TransformRigTool.counterDelimiter", ' ')
    self.directionSmartbone = prefs:GetBool("MR_TransformRigTool.directionSmartbone", true)
    self.renameSmartbone = prefs:GetBool("MR_TransformRigTool.renameSmartbone", false)
    self.swapNames = prefs:GetBool("MR_TransformRigTool.swapNames", true)
end

function MR_TransformRigTool:SavePrefs(prefs)
	prefs:SetBool('MR_TransformRigTool.considerNewParentRotation', self.considerNewParentRotation)
	prefs:SetBool('MR_TransformRigTool.considerOldParentRotation', self.considerOldParentRotation)
	prefs:SetInt("MR_TransformRigTool.centerId", self.centerId)
	prefs:SetBool("MR_TransformRigTool.selectBonesBt", self.selectBonesBt)
    prefs:SetBool("MR_TransformRigTool.translateBt", self.translateBt)
    prefs:SetBool("MR_TransformRigTool.scaleBt", self.scaleBt)
    prefs:SetBool("MR_TransformRigTool.rotateBt", self.rotateBt)
    prefs:SetBool("MR_TransformRigTool.transformBones", self.transformBones)
    prefs:SetBool("MR_TransformRigTool.transformPoints", self.transformPoints)
    prefs:SetBool("MR_TransformRigTool.autoSelect", self.autoSelect)
    prefs:SetBool("MR_TransformRigTool.ignoreRefLayers", self.ignoreRefLayers)
    prefs:SetBool("MR_TransformRigTool.transformOrigin", self.transformOrigin)
    prefs:SetBool("MR_TransformRigTool.transformVitruvianBones", self.transformVitruvianBones)
    prefs:SetBool("MR_TransformRigTool.transformTargetBones", self.transformTargetBones)
    prefs:SetBool("MR_TransformRigTool.adjustStrokeWidth", self.adjustStrokeWidth)
    prefs:SetBool("MR_TransformRigTool.transformImageLayers", self.transformImageLayers)
    prefs:SetBool("MR_TransformRigTool.transformInfo", self.transformInfo)
    prefs:SetBool("MR_TransformRigTool.followPathAdaptation", self.followPathAdaptation)
    prefs:SetBool("MR_TransformRigTool.transformPatchLayers", self.transformPatchLayers)
    prefs:SetBool("MR_TransformRigTool.useGlobalFlip", self.useGlobalFlip)
    prefs:SetBool("MR_TransformRigTool.adaptiveScaleAndRotation", self.adaptiveScaleAndRotation)
    prefs:SetBool("MR_TransformRigTool.useLineWidthInsteadStrokeWidth", self.useLineWidthInsteadStrokeWidth)
    prefs:SetBool("MR_TransformRigTool.liveUpdatingReferences", self.liveUpdatingReferences)
    prefs:SetString("MR_TransformRigTool.duplicatedBodypartSuffix", self.duplicatedBodypartSuffix)
    prefs:SetBool("MR_TransformRigTool.directionMode", self.directionMode)
    prefs:SetBool("MR_TransformRigTool.rDirection", self.rDirection)
    prefs:SetBool("MR_TransformRigTool.lDirection", self.lDirection)
    prefs:SetBool("MR_TransformRigTool.customMode", self.customMode)
    prefs:SetBool("MR_TransformRigTool.suffixMode", self.suffixMode)
    prefs:SetString("MR_TransformRigTool.customModeInput1", self.customModeInput1)
    prefs:SetString("MR_TransformRigTool.customModeInput2", self.customModeInput2)
    prefs:SetString("MR_TransformRigTool.counterDelimiter", self.counterDelimiter)
    prefs:SetBool("MR_TransformRigTool.directionSmartbone", self.directionSmartbone)
    prefs:SetBool("MR_TransformRigTool.renameSmartbone", self.renameSmartbone)
    prefs:SetBool("MR_TransformRigTool.swapNames", self.swapNames)
end

function MR_TransformRigTool:ResetPrefs()
	MR_TransformRigTool.considerNewParentRotation = false
	MR_TransformRigTool.considerOldParentRotation = false
	self.centerId = 0
	self.selectBonesBt = true
    self.translateBt = false
    self.scaleBt = false
    self.rotateBt = false
    self.transformBones = false
    self.transformPoints = true
    self.autoSelect = false
    self.ignoreRefLayers = true
    self.transformOrigin = true
    self.transformVitruvianBones = true
    self.transformTargetBones = true
    self.adjustStrokeWidth = false
    self.transformImageLayers = false
    self.transformInfo = true
    self.followPathAdaptation = false
    self.transformPatchLayers = true
    self.useGlobalFlip = false
    self.adaptiveScaleAndRotation = false
    self.useLineWidthInsteadStrokeWidth = false
    self.liveUpdatingReferences = true
    self.duplicatedBodypartSuffix = ' Dup'
    self.directionMode = true
    self.rDirection = true
    self.lDirection = false
    self.customMode = false
    self.suffixMode = false
    self.customModeInput1 = 'Front'
    self.customModeInput2 = 'Back'
    self.counterDelimiter = ' '
    self.directionSmartbone = true
    self.renameSmartbone = false
    self.swapNames = true
end

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

MR_TransformRigTool.considerNewParentRotation = false
MR_TransformRigTool.considerOldParentRotation = false
MR_TransformRigTool.boneList = {}
MR_TransformRigTool.boneRefList = {}
MR_TransformRigTool.boneOrigPosList = {}
MR_TransformRigTool.boneOrigGlobalPosList = {}
MR_TransformRigTool.boneAngleList = {}
MR_TransformRigTool.boneAngleExtraList = {}
MR_TransformRigTool.boneAngleOffsetList = {}
MR_TransformRigTool.boneParentAngleList = {}
MR_TransformRigTool.boneParentList = {}
MR_TransformRigTool.boneParentMatrixList = {}
MR_TransformRigTool.boneLayerId = nil
MR_TransformRigTool.status = ''
MR_TransformRigTool.fixedBonesList = {}
MR_TransformRigTool.fixedActionsList = {}
MR_TransformRigTool.totalBones = 0
MR_TransformRigTool.refKey = 1
MR_TransformRigTool.offset = LM.Vector2:new_local()
MR_TransformRigTool.centerVec = LM.Vector2:new_local()
MR_TransformRigTool.scaling = LM.Vector2:new_local()
MR_TransformRigTool.centerId = 0
MR_TransformRigTool.transformType = 0
MR_TransformRigTool.selectBonesBt = true
MR_TransformRigTool.translateBt = false
MR_TransformRigTool.scaleBt = false
MR_TransformRigTool.rotateBt = false
MR_TransformRigTool.transformBones = false
MR_TransformRigTool.transformPoints = true
MR_TransformRigTool.lastScaleX = 1.0
MR_TransformRigTool.lastScaleY = 1.0
MR_TransformRigTool.isFlip = false
MR_TransformRigTool.bMin = LM.Vector2:new_local()
MR_TransformRigTool.bMax = LM.Vector2:new_local()
MR_TransformRigTool.isSkel = false
MR_TransformRigTool.autoSelect = false
MR_TransformRigTool.transformOrigin = true
MR_TransformRigTool.adjustStrokeWidth = false
MR_TransformRigTool.startAngle = 0
MR_TransformRigTool.lastVec = LM.Vector2:new_local()
MR_TransformRigTool.lastAngle = 0
MR_TransformRigTool.mainSkelSelectedBones = 0
MR_TransformRigTool.lastTransformMode = 0 -- 0=non, 1=transform, 2=scale, 3=scale.x, 4=scale.y, 5=rotate
MR_TransformRigTool.lastCenter = LM.Vector2:new_local()
MR_TransformRigTool.lastCenterId = LM.Vector2:new_local()
MR_TransformRigTool.lastTranslate = LM.Vector2:new_local()
MR_TransformRigTool.lastScaling = LM.Vector2:new_local()
MR_TransformRigTool.lastScaling:Set(1, 1)
MR_TransformRigTool.lastRotate = 0
MR_TransformRigTool.showTransformInfo = false
MR_TransformRigTool.mousePos = LM.Vector2:new_local()
MR_TransformRigTool.transformInfoText = ''
MR_TransformRigTool.ignoreRefLayers = true
MR_TransformRigTool.transformImageLayers = false
MR_TransformRigTool.refLayersList = {}
MR_TransformRigTool.drawingVec = LM.Vector2:new_local()
MR_TransformRigTool.drawingStartVec = LM.Vector2:new_local()
MR_TransformRigTool.transformCenter = LM.Vector2:new_local()
MR_TransformRigTool.blockTransformation = false
MR_TransformRigTool.transformVitruvianBones = true
MR_TransformRigTool.transformTargetBones = true
MR_TransformRigTool.protectLayerTransformation = false
MR_TransformRigTool.transformInfo = true
MR_TransformRigTool.followPathAdaptation = false
MR_TransformRigTool.transformPatchLayers = true
MR_TransformRigTool.useGlobalFlip = false
MR_TransformRigTool.adaptiveScaleAndRotation = false
MR_TransformRigTool.useLineWidthInsteadStrokeWidth = false
MR_TransformRigTool.liveUpdatingReferences = true
MR_TransformRigTool.newSmartBoneName = ''
MR_TransformRigTool.duplicatedBodypartSuffix = ' Dup'
MR_TransformRigTool.duplicateActionsMode = 1 -- 1 = to self skeleton, 2 = to another skeleton

MR_TransformRigTool.vectorLayersToStrokeChange = {}

MR_TransformRigTool.vectorLayersToChange = {}
MR_TransformRigTool.vectorLayersToChange.layer = {}
MR_TransformRigTool.vectorLayersToChange.parentalFlip = {}
MR_TransformRigTool.vectorLayersToChange.layersPosition = {}
MR_TransformRigTool.vectorLayersToChange.origin = {}
MR_TransformRigTool.vectorLayersToChange.transformOriginsOffset = {}
MR_TransformRigTool.followPathGroupsToChange = {}
MR_TransformRigTool.followPathGroupsToChange.group = {}
MR_TransformRigTool.followPathGroupsToChange.origin = {}
MR_TransformRigTool.followPathGroupsToChange.angle = {}
MR_TransformRigTool.followPathGroupsToChange.parentalFlip = {}
MR_TransformRigTool.followPathGroupsToChange.transform = {}
MR_TransformRigTool.followPathGroupsToChange.layersPosition = {}
MR_TransformRigTool.groupsToChange = {}
MR_TransformRigTool.groupsToChange.group = {}
MR_TransformRigTool.patchLayersToChange = {}
MR_TransformRigTool.patchLayersToChange.layer = {}
MR_TransformRigTool.imageLayersToChange = {}
MR_TransformRigTool.imageLayersToChange.layer = {}
MR_TransformRigTool.imageLayersToChange.origin = {}
MR_TransformRigTool.imageLayersToChange.scale = {}
MR_TransformRigTool.imageLayersToChange.position = {}
MR_TransformRigTool.imageLayersToChange.angle = {}
MR_TransformRigTool.imageLayersToChange.positionOffset = {}
MR_TransformRigTool.imageLayersToChange.parentalFlip = {}

MR_TransformRigTool.childBonesList = {}
MR_TransformRigTool.curvesList = {}
MR_TransformRigTool.fPoses = {}
MR_TransformRigTool.selList = {}

MR_TransformRigTool.refLayersList = {}
MR_TransformRigTool.bonesList = {}
MR_TransformRigTool.templayersList = {}
MR_TransformRigTool.targetBonesList = {}

MR_TransformRigTool.areBonesUnlinked = false
MR_TransformRigTool.bonesLinkList = {}
MR_TransformRigTool.bonesLinkList.bone = {}
MR_TransformRigTool.bonesLinkList.parentBone = {}
MR_TransformRigTool.smartbonesList = {}
MR_TransformRigTool.smartbonesList.originalName = {}
MR_TransformRigTool.smartbonesList.newName = {}
MR_TransformRigTool.smartbonesList.mode = {}
MR_TransformRigTool.targetSkelBonesNamesList = {}

MR_TransformRigTool.isVitruvianBonesAvaible = true
MR_TransformRigTool.isBones1354 = true

MR_TransformRigTool.directionMode = true
MR_TransformRigTool.rDirection = true
MR_TransformRigTool.lDirection = false
MR_TransformRigTool.customMode = false
MR_TransformRigTool.customModeInput1 = 'Front'
MR_TransformRigTool.customModeInput2 = 'Back'
MR_TransformRigTool.suffixMode = false
MR_TransformRigTool.directionSmartbone = true
MR_TransformRigTool.rDirectionSmartbone = true
MR_TransformRigTool.lDirectionSmartbone = false
MR_TransformRigTool.renameSmartbone = false
MR_TransformRigTool.swapNames = true

MR_TransformRigTool.counterDelimiter = ' '

-- **************************************************
-- MR_SettingsDialog
-- **************************************************

local MR_ActionsDialog = {}

MR_ActionsDialog.dynamicNumberText = {}
MR_ActionsDialog.dynamicSymbolText = {}
MR_ActionsDialog.actionInput = {}
MR_ActionsDialog.asActionRadioButton = {}
MR_ActionsDialog.asSmartboneRadioButton = {}
MR_ActionsDialog.removeRadioButton = {}
MR_ActionsDialog.edited = '*'
MR_ActionsDialog.notEdited = ' '
MR_ActionsDialog.chekNamesMode = false
MR_ActionsDialog.editedStatus = {}

MR_ActionsDialog.CHANGE = MOHO.MSG_BASE
MR_ActionsDialog.SET_R = MOHO.MSG_BASE + 1
MR_ActionsDialog.SET_R_ALT = MOHO.MSG_BASE + 2
MR_ActionsDialog.SET_L = MOHO.MSG_BASE + 3
MR_ActionsDialog.SET_L_ALT = MOHO.MSG_BASE + 4
MR_ActionsDialog.CHECK_NAMES = MOHO.MSG_BASE + 5
MR_ActionsDialog.CHECK_NAMES_ALT = MOHO.MSG_BASE + 6
MR_ActionsDialog.RESTORE_NAMES = MOHO.MSG_BASE + 7
MR_ActionsDialog.AUTO_MODE = MOHO.MSG_BASE + 8
MR_ActionsDialog.AUTO_MODE_ALT = MOHO.MSG_BASE + 9
MR_ActionsDialog.M1 = MOHO.MSG_BASE + 10
MR_ActionsDialog.M3 = MOHO.MSG_BASE + 11
MR_ActionsDialog.CHANGE_TEXT = MOHO.MSG_BASE + 12
MR_ActionsDialog.REPLACE_TEXT = MOHO.MSG_BASE + 13
MR_ActionsDialog.REPLACE_TEXT_ALT = MOHO.MSG_BASE + 14

function MR_ActionsDialog:new(moho)
    local d = LM.GUI.SimpleDialog(MR_TransformRigTool:Localize('UILabel'), MR_ActionsDialog)
    local l = d:GetLayout()
	
	local fileWord = MOHO.Localize("/Menus/File/File=File")
	
	d.dynamicHeaderText = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Smartbones Manager'), 0)
	l:AddChild(d.dynamicHeaderText, LM.GUI.ALIGN_CENTER, 0)
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
	l:PushH()
	for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
		d.editedStatus[i] = d.notEdited
		if #MR_TransformRigTool.smartbonesList.originalName > 60 then
			if i == 1 then
				l:PushV()
			elseif i == 26 then
				l:Pop()
				l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
				l:PushV()
			elseif i == 51 or i == 76 then
				l:Pop()
				l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
				l:PushV()
			elseif i == 101 then
				l:Pop()	
			end
			if i <= 100 then
				l:PushH()
					local numberText
					if i < 10 then
						numberText = '  '..i
					else
						numberText = ''..i
					end	
					local dynamicTxt = LM.GUI.DynamicText(numberText, 0)
					d.dynamicNumberText[i] = dynamicTxt
					l:AddChild(d.dynamicNumberText[i], LM.GUI.ALIGN_LEFT, 0)
					
					local symbolText = d.editedStatus[i]
					
					local dynamicSymbolTxt = LM.GUI.DynamicText(symbolText, 0)
					d.dynamicSymbolText[i] = dynamicSymbolTxt
					l:AddChild(d.dynamicSymbolText[i], LM.GUI.ALIGN_LEFT, 0)
					
					textInput = LM.GUI.TextControl(150, ' ', d.CHANGE, LM.GUI.FIELD_TEXT, '')
					d.actionInput[i] = textInput
					d.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('Smartbone name does not changed'))
					l:AddChild(d.actionInput[i], LM.GUI.ALIGN_LEFT, 0)
					
					l:AddPadding(1)

					local asActionRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('As Action'), d.CHANGE)
					d.asActionRadioButton[i] = asActionRB
					d.asActionRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Duplicate only action'))	
					l:AddChild(d.asActionRadioButton[i], LM.GUI.ALIGN_LEFT, 0)

					local asSmartboneRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('As Smartbone'), d.CHANGE)
					d.asSmartboneRadioButton[i] = asSmartboneRB
					d.asSmartboneRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Duplicate smartbone'))	
					l:AddChild(d.asSmartboneRadioButton[i], LM.GUI.ALIGN_LEFT, 0)

					local removeRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Remove'), d.CHANGE)
					d.removeRadioButton[i] = removeRB
					d.removeRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Do not duplicate action'))	
					l:AddChild(d.removeRadioButton[i], LM.GUI.ALIGN_LEFT, 0)
				l:Pop()
			end	
			if i == #MR_TransformRigTool.smartbonesList.originalName and i ~= 26 and i ~= 51 and i ~= 76 and i ~= 101 then
				l:Pop()
			end
		else
			if i == 1 then
				l:PushV()
			elseif i == 16 then
				l:Pop()
				l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
				l:PushV()
			elseif i == 31 or i == 46 then
				l:Pop()
				l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
				l:PushV()
			elseif i == 61 then
				l:Pop()	
			end
			if i <= 60 then
				l:PushH()
					local numberText
					if i < 10 then
						numberText = '  '..i
					else
						numberText = ''..i
					end	
					local dynamicTxt = LM.GUI.DynamicText(numberText, 0)
					d.dynamicNumberText[i] = dynamicTxt
					l:AddChild(d.dynamicNumberText[i], LM.GUI.ALIGN_LEFT, 0)
					
					local symbolText = d.editedStatus[i]
					
					local dynamicSymbolTxt = LM.GUI.DynamicText(symbolText, 0)
					d.dynamicSymbolText[i] = dynamicSymbolTxt
					l:AddChild(d.dynamicSymbolText[i], LM.GUI.ALIGN_LEFT, 0)
					
					textInput = LM.GUI.TextControl(150, ' ', d.CHANGE, LM.GUI.FIELD_TEXT, '')
					d.actionInput[i] = textInput
					d.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('Smartbone name does not changed'))	
					l:AddChild(d.actionInput[i], LM.GUI.ALIGN_LEFT, 0)
					
					l:AddPadding(1)

					local asActionRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('As Action'), d.CHANGE)
					d.asActionRadioButton[i] = asActionRB
					d.asActionRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Duplicate only action'))	
					l:AddChild(d.asActionRadioButton[i], LM.GUI.ALIGN_LEFT, 0)

					local asSmartboneRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('As Smartbone'), d.CHANGE)
					d.asSmartboneRadioButton[i] = asSmartboneRB
					d.asSmartboneRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Duplicate smartbone'))	
					l:AddChild(d.asSmartboneRadioButton[i], LM.GUI.ALIGN_LEFT, 0)

					local removeRB = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Remove'), d.CHANGE)
					d.removeRadioButton[i] = removeRB
					d.removeRadioButton[i]:SetToolTip(MR_TransformRigTool:Localize('Do not duplicate action'))	
					l:AddChild(d.removeRadioButton[i], LM.GUI.ALIGN_LEFT, 0)
				l:Pop()
			end
			if i == #MR_TransformRigTool.smartbonesList.originalName and i ~= 16 and i ~= 31 and i ~= 46 and i ~= 61 then
				l:Pop()
			end
		end
	end	
	l:Pop()
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
	l:PushH()
        d.RButton = LM.GUI.Button(MR_TransformRigTool:Localize('Set R'), d.SET_R)
		d.RButton:SetAlternateMessage(d.SET_R_ALT)
        d.RButton:SetToolTip(MR_TransformRigTool:Localize('R to L Tooltip'))
        l:AddChild(d.RButton, LM.GUI.ALIGN_LEFT, 0)

        d.LButton = LM.GUI.Button(MR_TransformRigTool:Localize('Set L'), d.SET_L)
		d.LButton:SetAlternateMessage(d.SET_L_ALT)
		d.LButton:SetToolTip(MR_TransformRigTool:Localize('L to R Tooltip'))
        l:AddChild(d.LButton, LM.GUI.ALIGN_LEFT, 0)
		
		d.autoModeButton = LM.GUI.Button(MR_TransformRigTool:Localize('Auto Mode'), d.AUTO_MODE)
		d.autoModeButton:SetToolTip(MR_TransformRigTool:Localize('Suggest Tooltip'))
		d.autoModeButton:SetAlternateMessage(d.AUTO_MODE_ALT)
        l:AddChild(d.autoModeButton, LM.GUI.ALIGN_LEFT, 0)
		
		d.m1Button = LM.GUI.Button(' * ', d.M1)
		d.m1Button:SetToolTip(MR_TransformRigTool:Localize('Duplicate Actions Tooltip'))
        l:AddChild(d.m1Button, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(3)
		else
			l:AddPadding(14)
		end
				
		d.m3Button = LM.GUI.Button(' - ', d.M3)
		d.m3Button:SetToolTip(MR_TransformRigTool:Localize('Remove Actions Tooltip'))
        l:AddChild(d.m3Button, LM.GUI.ALIGN_LEFT, 0)
    l:Pop()
	
	l:PushH()
		d.replaceTextInput1 = LM.GUI.TextControl(100, ' ', d.CHANGE_TEXT, LM.GUI.FIELD_TEXT, '')
		d.replaceTextInput1:SetToolTip(MR_TransformRigTool:Localize('Replace Text 1 Tooltip'))	
		l:AddChild(d.replaceTextInput1, LM.GUI.ALIGN_LEFT, 0)
		
		d.replaceTextInput2 = LM.GUI.TextControl(100, ' ', d.CHANGE_TEXT, LM.GUI.FIELD_TEXT, '')
		d.replaceTextInput2:SetToolTip(MR_TransformRigTool:Localize('Replace Text 2 Tooltip'))	
		l:AddChild(d.replaceTextInput2, LM.GUI.ALIGN_LEFT, 0)
		
		d.replaceTextButton = LM.GUI.Button(MR_TransformRigTool:Localize('Replace Text'), d.REPLACE_TEXT)
		d.replaceTextButton:SetToolTip(MR_TransformRigTool:Localize('Replace Text Button Tooltip'))	
		d.replaceTextButton:SetAlternateMessage(d.REPLACE_TEXT_ALT)
        l:AddChild(d.replaceTextButton, LM.GUI.ALIGN_LEFT, 0)
	l:Pop()
	 
	l:PushH()
        d.checkNamesButton = LM.GUI.Button(MR_TransformRigTool:Localize('Check Names'), d.CHECK_NAMES)
        d.checkNamesButton:SetToolTip(MR_TransformRigTool:Localize('Check Names Tooltip'))
		d.checkNamesButton:SetAlternateMessage(d.CHECK_NAMES_ALT)
        l:AddChild(d.checkNamesButton, LM.GUI.ALIGN_LEFT, 0)
		
		l:AddPadding(2)
		
		d.restoreNamesButton = LM.GUI.Button(MR_TransformRigTool:Localize('Reset Names'), d.RESTORE_NAMES)
		d.restoreNamesButton:SetToolTip(MR_TransformRigTool:Localize('Reset Names Tooltip'))
        l:AddChild(d.restoreNamesButton, LM.GUI.ALIGN_LEFT, 0)
	l:Pop()	
    return d
end

function MR_ActionsDialog:UpdateWidgets(moho)
	for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
		if i <= 100 then
			self.actionInput[i]:SetValue(MR_TransformRigTool.smartbonesList.originalName[i])
			self.asActionRadioButton[i]:SetValue(false)
			self.asSmartboneRadioButton[i]:SetValue(true)
			self.removeRadioButton[i]:SetValue(false)
		end	
	end
	self.replaceTextInput1:SetValue(MR_TransformRigTool.customModeInput1)
	self.replaceTextInput2:SetValue(MR_TransformRigTool.customModeInput2)
end

function MR_ActionsDialog:OnOK(moho)
	for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
		if i <= 100 then
			MR_TransformRigTool.smartbonesList.newName[i] = self.actionInput[i]:Value()
			if self.asActionRadioButton[i]:Value() then
				MR_TransformRigTool.smartbonesList.mode[i] = 1
			elseif self.asSmartboneRadioButton[i]:Value() then
				MR_TransformRigTool.smartbonesList.mode[i] = 2
			elseif self.removeRadioButton[i]:Value() then
				MR_TransformRigTool.smartbonesList.mode[i] = 3
			end	
		end	
	end	
end

function MR_ActionsDialog:CheckNameStatus()
	for i = 1, #self.actionInput do
		if i <= 100 then
			local newName = self.actionInput[i]:Value()
			if newName ~= MR_TransformRigTool.smartbonesList.originalName[i] then
				self.editedStatus[i] = self.edited
				self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('Smartbone name is changed'))
			else
				self.editedStatus[i] = self.notEdited
				self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('Smartbone name does not changed'))
			end
			self.dynamicSymbolText[i]:SetValue(self.editedStatus[i])
		end	
	end
end

function MR_ActionsDialog:HandleMessage(msg)
    if msg == self.CHANGE then
		self:CheckNameStatus()
    elseif msg == self.SET_R then
		for i = 1, #self.actionInput do
			if i <= 100 then
				local newName = self.actionInput[i]:Value()
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'L', 'R')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'l', 'r')
				self.actionInput[i]:SetValue(newName)
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.SET_R_ALT then
		for i = 1, #self.actionInput do
			if i <= 100 then
				local newName = self.actionInput[i]:Value()
				local tempText1 = 'TEMPTEXT1'
				local tempText2 = 'TEMPTEXT2'
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'L', tempText1)
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'R', tempText2)
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, tempText1, 'R')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, tempText2, 'L')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'l', string.lower(tempText1))
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'r', string.lower(tempText2))
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, string.lower(tempText1), 'r')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, string.lower(tempText2), 'l')
				self.actionInput[i]:SetValue(newName)
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.SET_L then
		for i = 1, #self.actionInput do
			if i <= 100 then
				local newName = self.actionInput[i]:Value()
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'R', 'L')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'r', 'l')
				self.actionInput[i]:SetValue(newName)
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.SET_L_ALT then
		for i = 1, #self.actionInput do
			if i <= 100 then
				local newName = self.actionInput[i]:Value()
				local tempText1 = 'TEMPTEXT1'
				local tempText2 = 'TEMPTEXT2'
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'R', tempText1)
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'L', tempText2)
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, tempText1, 'L')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, tempText2, 'R')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'r', string.lower(tempText1))
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, 'l', string.lower(tempText2))
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, string.lower(tempText1), 'l')
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, string.lower(tempText2), 'r')
				self.actionInput[i]:SetValue(newName)
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.AUTO_MODE then
		for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
			if i <= 100 then
				local newName1 = MR_TransformRigTool.smartbonesList.originalName[i]
				local newName2 = MR_TransformRigTool.smartbonesList.originalName[i]
				newName1 = MR_TransformRigTool:ChangeNameString(moho, newName1, 'L', 'R')
				newName1 = MR_TransformRigTool:ChangeNameString(moho, newName1, 'l', 'r')
				newName2 = MR_TransformRigTool:ChangeNameString(moho, newName2, 'R', 'L')
				newName2 = MR_TransformRigTool:ChangeNameString(moho, newName2, 'r', 'l')
				if newName1 ~= MR_TransformRigTool.smartbonesList.originalName[i] or
					newName2 ~= MR_TransformRigTool.smartbonesList.originalName[i] then
					self.asSmartboneRadioButton[i]:SetValue(true)
				else
					self.asActionRadioButton[i]:SetValue(true)
				end
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.AUTO_MODE_ALT then
		for i = 1, #self.asSmartboneRadioButton do
			if i <= 100 then
				self.asSmartboneRadioButton[i]:SetValue(true)
			end	
		end
    elseif msg == self.CHECK_NAMES then
		if self.chekNamesMode then
			for i = 1, #self.actionInput do
				if i <= 100 then
					self.actionInput[i]:Enable(true)
				end	
			end
			self.chekNamesMode = false
			self:CheckNameStatus()
			return
		end
		self:CheckNameStatus()
		for i = 1, #self.actionInput do
			if i <= 100 then
				if self.actionInput[i]:Value() ~= MR_TransformRigTool.smartbonesList.newName[i] then
					self.chekNamesMode = true
					for b = 1, #MR_TransformRigTool.targetSkelBonesNamesList do
						local boneName = MR_TransformRigTool.targetSkelBonesNamesList[b]
						if MR_TransformRigTool.duplicateActionsMode == 1 then
							if self.asActionRadioButton[i]:Value() then
								self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
								self.actionInput[i]:Enable(false)
							elseif self.asSmartboneRadioButton[i]:Value() then
								if self.actionInput[i]:Value() == boneName then
									self.actionInput[i]:Enable(true)
									self.dynamicSymbolText[i]:SetValue('×')
									self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is used'))
									break
								else
									self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
									self.actionInput[i]:Enable(false)
								end
							elseif self.removeRadioButton[i]:Value() then
								self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
								self.actionInput[i]:Enable(false)
							end
						elseif MR_TransformRigTool.duplicateActionsMode == 2 then
							if self.asActionRadioButton[i]:Value() then
								self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
								self.actionInput[i]:Enable(false)
							elseif self.asSmartboneRadioButton[i]:Value() then	
								if self.actionInput[i]:Value() == boneName then
									self.actionInput[i]:Enable(true)
									self.dynamicSymbolText[i]:SetValue('×')
									self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is used'))
									break
								else
									self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
									self.actionInput[i]:Enable(false)
								end
							elseif self.removeRadioButton[i]:Value() then
								self.actionInput[i]:SetToolTip(MR_TransformRigTool:Localize('This smartbone name is not used'))
								self.actionInput[i]:Enable(false)
							end
						end
					end
				end
			end	
		end	
    elseif msg == self.CHECK_NAMES_ALT then
		for i = 1, #self.actionInput do
			if i <= 100 then
				self.actionInput[i]:Enable(true)
			end	
		end
		self:CheckNameStatus()
    elseif msg == self.RESTORE_NAMES then
		for i = 1, #self.actionInput do
			if i <= 100 then
				self.actionInput[i]:SetValue(MR_TransformRigTool.smartbonesList.originalName[i])
				self.actionInput[i]:Enable(true)
			end	
		end
		self:CheckNameStatus()
	elseif msg == self.M1 then
		for i = 1, #self.asActionRadioButton do
			if i <= 100 then
				self.asActionRadioButton[i]:SetValue(true)
			end	
		end
	elseif msg == self.M3 then
		for i = 1, #self.asActionRadioButton do
			if i <= 100 then
				self.removeRadioButton[i]:SetValue(true)
			end	
		end	
	elseif msg == self.REPLACE_TEXT then
		local text1 = self.replaceTextInput1:Value()
		local text2 = self.replaceTextInput2:Value()
		for i = 1, #self.actionInput do
			if i <= 100 then
				local newName = self.actionInput[i]:Value()
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, text1, text2)
				newName = MR_TransformRigTool:ChangeNameString(moho, newName, string.lower(text1), string.lower(text2))
				self.actionInput[i]:SetValue(newName)
			end	
		end
		self:CheckNameStatus()
	elseif msg == self.REPLACE_TEXT_ALT then
		local text1 = self.replaceTextInput1:Value()
		local text2 = self.replaceTextInput2:Value()
		self.replaceTextInput1:SetValue(text2)
		self.replaceTextInput2:SetValue(text1)
    end
end

local MR_DuplicateBodypartDialog = {} 

MR_DuplicateBodypartDialog.DIRECTION_MODE = MOHO.MSG_BASE
MR_DuplicateBodypartDialog.SWAP_MODE = MOHO.MSG_BASE + 1
MR_DuplicateBodypartDialog.R_TO_L = MOHO.MSG_BASE + 2
MR_DuplicateBodypartDialog.L_TO_R = MOHO.MSG_BASE + 3
MR_DuplicateBodypartDialog.CUSTOM_MODE = MOHO.MSG_BASE + 4
MR_DuplicateBodypartDialog.CUTOM_INPUT_1 = MOHO.MSG_BASE + 5
MR_DuplicateBodypartDialog.CUTOM_INPUT_2 = MOHO.MSG_BASE + 6
MR_DuplicateBodypartDialog.SUFFIX_MODE = MOHO.MSG_BASE + 7
MR_DuplicateBodypartDialog.SUFFIX = MOHO.MSG_BASE + 8
MR_DuplicateBodypartDialog.REVERSE = MOHO.MSG_BASE + 9

function MR_DuplicateBodypartDialog:new(moho)
	local d = LM.GUI.SimpleDialog(MR_TransformRigTool:Localize('UILabel'), MR_DuplicateBodypartDialog)
	local l = d:GetLayout()
	d.pleaseChoseHowToRenameBonesAndActionsText = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Please chose how to rename bones and actions:'), 0)
    l:AddChild(d.pleaseChoseHowToRenameBonesAndActionsText, LM.GUI.ALIGN_LEFT, 0)
	
	d.swapCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Swap Mode'), d.SWAP_MODE)
	d.swapCheck:SetToolTip(MR_TransformRigTool:Localize('Swap Mode Tooltip'))
	l:AddChild(d.swapCheck, LM.GUI.ALIGN_LEFT, 0)
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)

	local fileWord = MOHO.Localize("/Menus/File/File=File")

    l:PushH()
        d.directionModeRadioButton = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Direction Mode'), d.DIRECTION_MODE)
        d.directionModeRadioButton:SetToolTip(MR_TransformRigTool:Localize('Direction Mode Tooltip'))
        l:AddChild(d.directionModeRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(25)
		else	
			l:AddPadding(54)
		end	
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

        d.rToLCheck = LM.GUI.CheckBox('R ➞ L', d.R_TO_L)
        d.rToLCheck:SetToolTip(MR_TransformRigTool:Localize('R to L Tooltip'))
        l:AddChild(d.rToLCheck, LM.GUI.ALIGN_LEFT, 0)
		
		l:AddPadding(84)
		
        d.lToRCheck = LM.GUI.CheckBox('L ➞ R', d.L_TO_R)
        d.lToRCheck:SetToolTip(MR_TransformRigTool:Localize('L to R Tooltip'))
        l:AddChild(d.lToRCheck, LM.GUI.ALIGN_LEFT, 0)
    l:Pop()
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
    l:PushH()
        d.customModeRadioButton = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Custom Mode'), d.CUSTOM_MODE)
        d.customModeRadioButton:SetToolTip(MR_TransformRigTool:Localize('Custom Mode Tooltip'))
        l:AddChild(d.customModeRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(22)
		else	
			l:AddPadding(7)
		end	

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

        d.customInput1 = LM.GUI.TextControl(100, '', d.CUTOM_INPUT_1, LM.GUI.FIELD_TEXT, '')
        d.customInput1:SetToolTip(MR_TransformRigTool:Localize('Replace Text 1 Tooltip'))
        l:AddChild(d.customInput1, LM.GUI.ALIGN_LEFT, 0)

		d.reversrButton = LM.GUI.Button('↔', d.REVERSE)
		d.reversrButton:SetToolTip(MR_TransformRigTool:Localize('Reverse Tooltip'))
        l:AddChild(d.reversrButton, LM.GUI.ALIGN_LEFT, 0)
		
        d.customInput2 = LM.GUI.TextControl(100, '', d.CUTOM_INPUT_2, LM.GUI.FIELD_TEXT, '')
		d.customInput2:SetToolTip(MR_TransformRigTool:Localize('Replace Text 2 Tooltip'))
        l:AddChild(d.customInput2, LM.GUI.ALIGN_LEFT, 0)
    l:Pop()
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
    l:PushH()
        d.suffixModeRadioButton = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Suffix Mode'), d.SUFFIX_MODE)
        d.suffixModeRadioButton:SetToolTip(MR_TransformRigTool:Localize('Suffix Mode Tooltip'))
        l:AddChild(d.suffixModeRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(1)
		else
			l:AddPadding(55)
		end	
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

        d.suffixInput = LM.GUI.TextControl(100, '', d.SUFFIX, LM.GUI.FIELD_TEXT, MR_TransformRigTool:Localize(''))
        d.suffixInput:SetToolTip(MR_TransformRigTool:Localize('Suffix Tooltip'))
        l:AddChild(d.suffixInput, LM.GUI.ALIGN_LEFT, 0)
    l:Pop()
    return d
end

function MR_DuplicateBodypartDialog:UpdateWidgets()
	self.swapCheck:SetValue(MR_TransformRigTool.swapNames)
	self.directionModeRadioButton:SetValue(MR_TransformRigTool.directionMode)
    self.rToLCheck:SetValue(MR_TransformRigTool.rDirection)
    self.lToRCheck:SetValue(MR_TransformRigTool.lDirection)
    self.customModeRadioButton:SetValue(MR_TransformRigTool.customMode)
	self.customInput1:SetValue(MR_TransformRigTool.customModeInput1)
	self.customInput2:SetValue(MR_TransformRigTool.customModeInput2)
    self.suffixModeRadioButton:SetValue(MR_TransformRigTool.suffixMode)
    self.suffixInput:SetValue(MR_TransformRigTool.duplicatedBodypartSuffix)
	
	self.rToLCheck:Enable(MR_TransformRigTool.directionMode)
	self.lToRCheck:Enable(MR_TransformRigTool.directionMode)
	self.customInput1:Enable(MR_TransformRigTool.customMode)
	self.customInput2:Enable(MR_TransformRigTool.customMode)
	self.suffixInput:Enable(MR_TransformRigTool.suffixMode)
	self.reversrButton:Enable(MR_TransformRigTool.customMode)
end

function MR_DuplicateBodypartDialog:OnOK()
	MR_TransformRigTool.swapNames = self.swapCheck:Value()
	MR_TransformRigTool.directionMode = self.directionModeRadioButton:Value()
	MR_TransformRigTool.rDirection = self.rToLCheck:Value()
    MR_TransformRigTool.lDirection = self.lToRCheck:Value()
    MR_TransformRigTool.customMode = self.customModeRadioButton:Value()
	MR_TransformRigTool.customModeInput1 = self.customInput1:Value()
	MR_TransformRigTool.customModeInput2 = self.customInput2:Value()
    MR_TransformRigTool.suffixMode = self.suffixModeRadioButton:Value()
	if self.suffixInput:Value() ~= '' then
		MR_TransformRigTool.duplicatedBodypartSuffix = self.suffixInput:Value()
	else
		MR_TransformRigTool.duplicatedBodypartSuffix = ' '
	end
end

function MR_DuplicateBodypartDialog:HandleMessage(msg)
    if msg == self.SWAP_MODE then
    elseif msg == self.DIRECTION_MODE then
        self.directionModeRadioButton:SetValue(true)
        self.customModeRadioButton:SetValue(false)
        self.suffixModeRadioButton:SetValue(false)
		
		self.rToLCheck:Enable(true)
		self.lToRCheck:Enable(true)
		self.customInput1:Enable(false)
        self.customInput2:Enable(false)
        self.suffixInput:Enable(false)
        self.reversrButton:Enable(false)
    elseif msg == self.R_TO_L then
		self.rToLCheck:SetValue(true)
		self.lToRCheck:SetValue(false)
    elseif msg == self.L_TO_R then
		self.rToLCheck:SetValue(false)
		self.lToRCheck:SetValue(true)
    elseif msg == self.CUSTOM_MODE then
        self.directionModeRadioButton:SetValue(false)
        self.customModeRadioButton:SetValue(true)
        self.suffixModeRadioButton:SetValue(false)
		
		self.rToLCheck:Enable(false)
		self.lToRCheck:Enable(false)
		self.customInput1:Enable(true)
        self.customInput2:Enable(true)
        self.suffixInput:Enable(false)
        self.reversrButton:Enable(true)
    elseif msg == self.CUTOM_INPUT_1 then
    elseif msg == self.CUTOM_INPUT_2 then
    elseif msg == self.REVERSE then
		local text1 = self.customInput1:Value()
		local text2 = self.customInput2:Value()
		self.customInput1:SetValue(text2)
        self.customInput2:SetValue(text1)
    elseif msg == self.SUFFIX_MODE then
        self.directionModeRadioButton:SetValue(false)
        self.customModeRadioButton:SetValue(false)
        self.suffixModeRadioButton:SetValue(true)
		
		self.rToLCheck:Enable(false)
		self.lToRCheck:Enable(false)
		self.customInput1:Enable(false)
        self.customInput2:Enable(false)
        self.suffixInput:Enable(true)
        self.reversrButton:Enable(false)
    elseif msg == self.SUFFIX then
    end
end

local MR_RenameSmartBoneDialog = {} 

MR_RenameSmartBoneDialog.DIRECTION_MODE = MOHO.MSG_BASE
MR_RenameSmartBoneDialog.RENAME_SMARTBONE = MOHO.MSG_BASE + 1
MR_RenameSmartBoneDialog.R_TO_L = MOHO.MSG_BASE + 2
MR_RenameSmartBoneDialog.L_TO_R = MOHO.MSG_BASE + 3

function MR_RenameSmartBoneDialog:new(moho, name)
	MR_TransformRigTool.newSmartBoneName = name
	local d = LM.GUI.SimpleDialog(MR_TransformRigTool:Localize('UILabel'), MR_RenameSmartBoneDialog)
	local l = d:GetLayout()

	local fileWord = MOHO.Localize("/Menus/File/File=File")
	
	d.pleaseChoseHowToRenameSmartboneText = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Please chose how to rename smartbone:'), 0)
    l:AddChild(d.pleaseChoseHowToRenameSmartboneText, LM.GUI.ALIGN_LEFT, 0)
	
	l:PushH()
		d.directionModeRadioButton = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Direction Mode'), d.DIRECTION_MODE)
		d.directionModeRadioButton:SetToolTip(MR_TransformRigTool:Localize('Direction Mode Tooltip'))
        l:AddChild(d.directionModeRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(1)
		else
			l:AddPadding(34)
		end	
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

        d.rToLCheck = LM.GUI.CheckBox('R ➞ L', d.R_TO_L)
        d.rToLCheck:SetToolTip(MR_TransformRigTool:Localize('R to L Tooltip'))
        l:AddChild(d.rToLCheck, LM.GUI.ALIGN_LEFT, 0)
		
		l:AddPadding(30)
		
        d.lToRCheck = LM.GUI.CheckBox('L ➞ R', d.L_TO_R)
        d.lToRCheck:SetToolTip(MR_TransformRigTool:Localize('L to R Tooltip'))
        l:AddChild(d.lToRCheck, LM.GUI.ALIGN_LEFT, 0)
	l:Pop()
	
	l:PushH()
        d.renameSmartboneRadioButton = LM.GUI.RadioButton(MR_TransformRigTool:Localize('Rename Smartbone'), d.RENAME_SMARTBONE)
        d.renameSmartboneRadioButton:SetToolTip(MR_TransformRigTool:Localize('Rename Smartbone Tooltip'))
        l:AddChild(d.renameSmartboneRadioButton, LM.GUI.ALIGN_LEFT, 0)
		
		if fileWord == "Файл" then
			l:AddPadding(21)
		else
			l:AddPadding(1)
		end
		
		l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

        d.textInput = LM.GUI.TextControl(150, name)
        d.textInput:SetToolTip(MR_TransformRigTool:Localize('New Smartbone Name Tooltip'))
		l:AddChild(d.textInput, LM.GUI.ALIGN_LEFT)	
    l:Pop()
	return d
end

function MR_RenameSmartBoneDialog:UpdateWidgets()
	self.directionModeRadioButton:SetValue(MR_TransformRigTool.directionSmartbone)
	self.rToLCheck:SetValue(MR_TransformRigTool.rDirectionSmartbone)
	self.lToRCheck:SetValue(MR_TransformRigTool.lDirectionSmartbone)
	self.renameSmartboneRadioButton:SetValue(MR_TransformRigTool.renameSmartbone)
	
	self.rToLCheck:Enable(MR_TransformRigTool.directionSmartbone)
	self.lToRCheck:Enable(MR_TransformRigTool.directionSmartbone)
	self.textInput:Enable(MR_TransformRigTool.renameSmartbone)
end

function MR_RenameSmartBoneDialog:OnOK()
	if self.textInput:Value() ~= '' then
		MR_TransformRigTool.newSmartBoneName = self.textInput:Value()
	end
	MR_TransformRigTool.directionSmartbone = self.directionModeRadioButton:Value()
	MR_TransformRigTool.rDirectionSmartbone = self.rToLCheck:Value()
	MR_TransformRigTool.lDirectionSmartbone = self.lToRCheck:Value()
	MR_TransformRigTool.renameSmartbone = self.renameSmartboneRadioButton:Value()
end

function MR_RenameSmartBoneDialog:HandleMessage(msg)
    if msg == self.DIRECTION_MODE then
        self.directionModeRadioButton:SetValue(true)
        self.renameSmartboneRadioButton:SetValue(false)
		
		self.rToLCheck:Enable(true)
		self.lToRCheck:Enable(true)
		self.textInput:Enable(false)
    elseif msg == self.R_TO_L then
		self.rToLCheck:SetValue(true)
		self.lToRCheck:SetValue(false)
    elseif msg == self.L_TO_R then
		self.rToLCheck:SetValue(false)
		self.lToRCheck:SetValue(true)
    elseif msg == self.RENAME_SMARTBONE then
		self.directionModeRadioButton:SetValue(false)
        self.renameSmartboneRadioButton:SetValue(true)
		
		self.rToLCheck:Enable(false)
		self.lToRCheck:Enable(false)
		self.textInput:Enable(true)
	end	
end

local MR_SettingsDialog = {}

MR_SettingsDialog.SHOW_TRANSFORM_INFO = MOHO.MSG_BASE
MR_SettingsDialog.USE_GLOBAL_FLIP = MOHO.MSG_BASE + 1
MR_SettingsDialog.ADAPTIVE_SCALE_AND_ROTATION = MOHO.MSG_BASE + 2
MR_SettingsDialog.USE_LINE_WIDTH = MOHO.MSG_BASE + 3
MR_SettingsDialog.LIVE_UPDATING_REFERENCES = MOHO.MSG_BASE + 4
MR_SettingsDialog.DUPLICATED_BODYPART_SUFFIX = MOHO.MSG_BASE + 5
MR_SettingsDialog.COUNTER_DELIMITER = MOHO.MSG_BASE + 6
MR_SettingsDialog.CONSIDER_NEW_PARENT_ROTATION = MOHO.MSG_BASE + 7
MR_SettingsDialog.CONSIDER_OLD_PARENT_ROTATION = MOHO.MSG_BASE + 8

function MR_SettingsDialog:new()
    local d = LM.GUI.SimpleDialog(MR_TransformRigTool:Localize('UILabel'), MR_SettingsDialog)
    local l = d:GetLayout()

	d.statusText = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Live transformation settings:'), nil)
    l:AddChild(d.statusText, LM.GUI.ALIGN_LEFT, 0)
	
	if HV_Font then
		d.showTransformInfoCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Show Transform Info'), self.SHOW_TRANSFORM_INFO)
		l:AddChild(d.showTransformInfoCheck, LM.GUI.ALIGN_LEFT, 0)
	end	
	
	d.useGlobalFlipCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Use Global Flip'), self.USE_GLOBAL_FLIP)
    l:AddChild(d.useGlobalFlipCheck, LM.GUI.ALIGN_LEFT, 0)
	
	d.adaptiveScaleAndRotationCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Adaptive scale and rotation'), self.ADAPTIVE_SCALE_AND_ROTATION)
    l:AddChild(d.adaptiveScaleAndRotationCheck, LM.GUI.ALIGN_LEFT, 0)
	
	d.useLineWidthInsteadStrokeWidthCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Use Line Width'), self.USE_LINE_WIDTH)
    l:AddChild(d.useLineWidthInsteadStrokeWidthCheck, LM.GUI.ALIGN_LEFT, 0)
	
	d.liveUpdatingReferencesCheck = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Live updating references'), self.LIVE_UPDATING_REFERENCES)
    l:AddChild(d.liveUpdatingReferencesCheck, LM.GUI.ALIGN_LEFT, 0)
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)
	
	d.statusText2 = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Duplicate bodypart settings:'), nil)
    l:AddChild(d.statusText2, LM.GUI.ALIGN_LEFT, 0)
	
	d.duplicatedBodypartSuffixInput = LM.GUI.TextControl(75, '', self.ADAPTIVE_SCALE_AND_ROTATION, LM.GUI.FIELD_TEXT, MR_TransformRigTool:Localize('Duplicated names suffix'))
    l:AddChild(d.duplicatedBodypartSuffixInput, LM.GUI.ALIGN_LEFT, 0)
	
	d.counterDelimiterInput = LM.GUI.TextControl(51, '', self.COUNTER_DELIMITER, LM.GUI.FIELD_TEXT, MR_TransformRigTool:Localize('Counter delimiter'))
    l:AddChild(d.counterDelimiterInput, LM.GUI.ALIGN_LEFT, 0)
	
	l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL)

	d.statusText3 = LM.GUI.DynamicText(MR_TransformRigTool:Localize('Advaned mode settings:'), nil)
    l:AddChild(d.statusText3, LM.GUI.ALIGN_LEFT, 0)

    d.considerNewParentRotationCheckbox = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Consider new parent rotation'), d.CONSIDER_NEW_PARENT_ROTATION)
    l:AddChild(d.considerNewParentRotationCheckbox, LM.GUI.ALIGN_LEFT, 0)

    d.considerOldParentRotationCheckbox = LM.GUI.CheckBox(MR_TransformRigTool:Localize('Consider old parent rotation'), d.CONSIDER_OLD_PARENT_ROTATION)
    l:AddChild(d.considerOldParentRotationCheckbox, LM.GUI.ALIGN_LEFT, 0)
    return d
end

function MR_SettingsDialog:UpdateWidgets(moho)
	if HV_Font then
		self.showTransformInfoCheck:SetValue(MR_TransformRigTool.transformInfo)
	end	
    self.useGlobalFlipCheck:SetValue(MR_TransformRigTool.useGlobalFlip)
    self.adaptiveScaleAndRotationCheck:SetValue(MR_TransformRigTool.adaptiveScaleAndRotation)
    self.useLineWidthInsteadStrokeWidthCheck:SetValue(MR_TransformRigTool.useLineWidthInsteadStrokeWidth)
    self.liveUpdatingReferencesCheck:SetValue(MR_TransformRigTool.liveUpdatingReferences)
    self.duplicatedBodypartSuffixInput:SetValue(MR_TransformRigTool.duplicatedBodypartSuffix)
    self.counterDelimiterInput:SetValue(MR_TransformRigTool.counterDelimiter)
    self.considerNewParentRotationCheckbox:SetValue(MR_TransformRigTool.considerNewParentRotation)
    self.considerOldParentRotationCheckbox:SetValue(MR_TransformRigTool.considerOldParentRotation)
end

function MR_SettingsDialog:OnOK(moho)
	if HV_Font then
		MR_TransformRigTool.transformInfo = self.showTransformInfoCheck:Value()
	end	
    MR_TransformRigTool.useGlobalFlip = self.useGlobalFlipCheck:Value()
    MR_TransformRigTool.adaptiveScaleAndRotation = self.adaptiveScaleAndRotationCheck:Value()
    MR_TransformRigTool.useLineWidthInsteadStrokeWidth = self.useLineWidthInsteadStrokeWidthCheck:Value()
    MR_TransformRigTool.liveUpdatingReferences = self.liveUpdatingReferencesCheck:Value()
	
	if self.duplicatedBodypartSuffixInput:Value() ~= '' then
		MR_TransformRigTool.duplicatedBodypartSuffix = self.duplicatedBodypartSuffixInput:Value()
	else
		MR_TransformRigTool.duplicatedBodypartSuffix = ' '
	end
	
	MR_TransformRigTool.counterDelimiter = self.counterDelimiterInput:Value()
    MR_TransformRigTool.considerNewParentRotation = self.considerNewParentRotationCheckbox:Value()
    MR_TransformRigTool.considerOldParentRotation = self.considerOldParentRotationCheckbox:Value()
end

function MR_SettingsDialog:HandleMessage(msg)
    if msg == self.SHOW_TRANSFORM_INFO then
		if HV_Font then
			MR_TransformRigTool.transformInfo = self.showTransformInfoCheck:Value()
		end	
    elseif msg == self.USE_GLOBAL_FLIP then
		MR_TransformRigTool.useGlobalFlip = self.useGlobalFlipCheck:Value()
    elseif msg == self.ADAPTIVE_SCALE_AND_ROTATION then
		MR_TransformRigTool.adaptiveScaleAndRotation = self.adaptiveScaleAndRotationCheck:Value()
    elseif msg == self.USE_LINE_WIDTH then
		MR_TransformRigTool.useLineWidthInsteadStrokeWidth = self.useLineWidthInsteadStrokeWidthCheck:Value()
    elseif msg == self.LIVE_UPDATING_REFERENCES then
		MR_TransformRigTool.liveUpdatingReferences = self.liveUpdatingReferencesCheck:Value()
    elseif msg == self.DUPLICATED_BODYPART_SUFFIX then
		MR_TransformRigTool.duplicatedBodypartSuffix = duplicatedBodypartSuffixInput:Value()
    elseif msg == self.COUNTER_DELIMITER then
		MR_TransformRigTool.counterDelimiter = self.counterDelimiterInput:Value()
    elseif msg == self.CONSIDER_NEW_PARENT_ROTATION then
		self.considerNewParentRotation = self.considerNewParentRotationCheckbox:Value()
    elseif msg == self.CONSIDER_OLD_PARENT_ROTATION then
        self.considerOldParentRotation = self.considerOldParentRotationCheckbox:Value()
    end
end

-- **************************************************
-- editLestTransformDialog
-- **************************************************

local MR_EditLastTransformDialog = {}

MR_EditLastTransformDialog.X = MOHO.MSG_BASE
MR_EditLastTransformDialog.Y = MOHO.MSG_BASE + 1
MR_EditLastTransformDialog.ANGLE = MOHO.MSG_BASE + 2
MR_EditLastTransformDialog.APPLY = MOHO.MSG_BASE + 3
MR_EditLastTransformDialog.APPLY_ALT = MOHO.MSG_BASE + 4

function MR_EditLastTransformDialog:new()
    local d = LM.GUI.SimpleDialog(MR_TransformRigTool:Localize('UILabel'), MR_EditLastTransformDialog)
    local l = d:GetLayout()

	d.xInput = LM.GUI.TextControl(0, '0.0000', d.X, LM.GUI.FIELD_FLOAT, MR_TransformRigTool:Localize('X'))
	d.xInput:SetWheelInc(0.01)
	l:AddChild(d.xInput, LM.GUI.ALIGN_LEFT, 0)

	d.yInput = LM.GUI.TextControl(0, '0.0000', d.Y, LM.GUI.FIELD_FLOAT, MR_TransformRigTool:Localize('Y'))
	d.yInput:SetWheelInc(0.1)
	l:AddChild(d.yInput, LM.GUI.ALIGN_LEFT, 0)

	d.angleInput = LM.GUI.TextControl(0, '0.0000', d.ANGLE, LM.GUI.FIELD_FLOAT, MR_TransformRigTool:Localize('Angle'))
	d.angleInput:SetWheelInc(1)
	l:AddChild(d.angleInput, LM.GUI.ALIGN_LEFT, 0)
	
    d.applyButton = LM.GUI.Button(MR_TransformRigTool:Localize('Apply'), d.APPLY)
	d.applyButton:SetAlternateMessage(d.APPLY_ALT)
    l:AddChild(d.applyButton, LM.GUI.ALIGN_LEFT, 0)
    return d
end

function MR_EditLastTransformDialog:UpdateWidgets(moho)
	if MR_TransformRigTool.translateBt then
		self.xInput:SetValue(MR_TransformRigTool.lastTranslate.x or 0)
		self.yInput:SetValue(MR_TransformRigTool.lastTranslate.y or 0)
		self.xInput:Enable(true)
		self.yInput:Enable(true)
		self.angleInput:Enable(false)
		self.angleInput:SetValue('')
	elseif MR_TransformRigTool.scaleBt then
		self.xInput:SetValue(MR_TransformRigTool.lastScaling.x or 1)
		self.yInput:SetValue(MR_TransformRigTool.lastScaling.y or 1)
		self.angleInput:SetValue(math.deg(MR_TransformRigTool.lastAngle) or 0)
		self.xInput:Enable(true)
		self.yInput:Enable(true)
		self.angleInput:Enable(false)
		self.angleInput:SetValue('')
	elseif MR_TransformRigTool.rotateBt then
		local angle = MR_TransformRigTool.lastAngle or 0
		self.angleInput:SetValue(math.deg(angle))
		self.xInput:SetValue('')
		self.yInput:SetValue('')
		self.xInput:Enable(false)
		self.yInput:Enable(false)
		self.angleInput:Enable(true)
	end	
end

function MR_EditLastTransformDialog:OnOK(moho)
end

function MR_EditLastTransformDialog:HandleMessage(msg)
    if msg == self.X then
    elseif msg == self.Y then
	elseif msg == self.ANGLE then
    elseif msg == self.APPLY then
		if MR_TransformRigTool.translateBt then
			MR_TransformRigTool.lastTranslate:Set(self.xInput:FloatValue(), self.yInput:FloatValue())
			self.xInput:SetValue(MR_TransformRigTool.lastTranslate.x)
			self.yInput:SetValue(MR_TransformRigTool.lastTranslate.y)
			MR_TransformRigTool.lastTransformMode = 1
			MR_TransformRigTool.repeatLastTransformationButton:SetToolTip(MR_TransformRigTool:Localize('Apply Custom Translate'))
		elseif MR_TransformRigTool.scaleBt then
			MR_TransformRigTool.lastScaling:Set(self.xInput:FloatValue(), self.yInput:FloatValue())
			self.xInput:SetValue(MR_TransformRigTool.lastScaling.x)
			self.yInput:SetValue(MR_TransformRigTool.lastScaling.y)
			MR_TransformRigTool.lastTransformMode = 2
			MR_TransformRigTool.updateCenterButton:Enable(true)
			MR_TransformRigTool.repeatLastTransformationButton:SetToolTip(MR_TransformRigTool:Localize('Apply Custom Scale'))
		elseif MR_TransformRigTool.rotateBt then	
			MR_TransformRigTool.lastAngle = math.rad(self.angleInput:FloatValue())
			self.angleInput:SetValue(math.deg(MR_TransformRigTool.lastAngle))
			MR_TransformRigTool.lastTransformMode = 5
			MR_TransformRigTool.updateCenterButton:Enable(true)
			MR_TransformRigTool.repeatLastTransformationButton:SetToolTip(MR_TransformRigTool:Localize('Apply Custom Rotation'))
		end
		MR_TransformRigTool.repeatLastTransformationButton:Enable(true)
		MR_TransformRigTool.lastCenterId = MR_TransformRigTool.centerId
	elseif msg == self.APPLY_ALT then
		self.yInput:SetValue(self.xInput:FloatValue())
    end
end

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

MR_TransformRigTool.ADVANCED_MODE = MOHO.MSG_BASE
MR_TransformRigTool.FIX_ACTIONS = MOHO.MSG_BASE + 1
MR_TransformRigTool.CLEAR = MOHO.MSG_BASE + 2
MR_TransformRigTool.CUSTOM_CENTER = MOHO.MSG_BASE + 3
MR_TransformRigTool.SELECTED_BONE = MOHO.MSG_BASE + 4
MR_TransformRigTool.SET_FROM_SELECTION = MOHO.MSG_BASE + 5
MR_TransformRigTool.SET_FROM_SELECTION_ALT = MOHO.MSG_BASE + 6
MR_TransformRigTool.DUPLICATE_BODYPART = MOHO.MSG_BASE + 7
MR_TransformRigTool.COPY_BODYPART = MOHO.MSG_BASE + 8
MR_TransformRigTool.FLIP_H = MOHO.MSG_BASE + 9
MR_TransformRigTool.FLIP_H_ALT = MOHO.MSG_BASE + 10
MR_TransformRigTool.FLIP_V = MOHO.MSG_BASE + 11
MR_TransformRigTool.FLIP_V_ALT = MOHO.MSG_BASE + 12
MR_TransformRigTool.DUPLICATE_AND_FLIP_SMARTBONE = MOHO.MSG_BASE + 13
MR_TransformRigTool.DUPLICATE_AND_FLIP_SMARTBONE_ALT = MOHO.MSG_BASE + 14
MR_TransformRigTool.RECONNECT_BONES = MOHO.MSG_BASE + 15
MR_TransformRigTool.RECONNECT_BONES_ALT = MOHO.MSG_BASE + 16
MR_TransformRigTool.SELECT_ALL_POINTS = MOHO.MSG_BASE + 17
MR_TransformRigTool.DESELECT_ALL_POINTS = MOHO.MSG_BASE + 18
MR_TransformRigTool.CHECK_FOR_SPLIT_DIMENSIONS = MOHO.MSG_BASE + 19
MR_TransformRigTool.IGNORE_REF = MOHO.MSG_BASE + 20
MR_TransformRigTool.TRANSFORM_POINTS = MOHO.MSG_BASE + 21
MR_TransformRigTool.AUTO_SELECT_POINTS = MOHO.MSG_BASE + 22
MR_TransformRigTool.ADJUST_STROKE_WIDTH = MOHO.MSG_BASE + 23
MR_TransformRigTool.FOLLOW_PATH_ADAPTATION = MOHO.MSG_BASE + 24
MR_TransformRigTool.FOLLOW_PATH_ADAPTATION_ALT = MOHO.MSG_BASE + 25
MR_TransformRigTool.TRANSFORM_BONES = MOHO.MSG_BASE + 26
MR_TransformRigTool.TRANSFORM_VITRUVIAN_BONES = MOHO.MSG_BASE + 27
MR_TransformRigTool.TRANSFORM_TARGET_BONES = MOHO.MSG_BASE + 28
MR_TransformRigTool.TRANSFORM_PATCH_LAYERS = MOHO.MSG_BASE + 29
MR_TransformRigTool.TRANSFORM_IMAGE_LAYERS = MOHO.MSG_BASE + 30
MR_TransformRigTool.TRANSFORM_ORIGIN = MOHO.MSG_BASE + 31
MR_TransformRigTool.SELECT_BONE = MOHO.MSG_BASE + 32
MR_TransformRigTool.TRANSLATE_BONE = MOHO.MSG_BASE + 33
MR_TransformRigTool.ROTATE_BONE = MOHO.MSG_BASE + 34
MR_TransformRigTool.SCALE_BONE = MOHO.MSG_BASE + 35
MR_TransformRigTool.UPDATE_CENTER = MOHO.MSG_BASE + 36
MR_TransformRigTool.UPDATE_CENTER_ALT = MOHO.MSG_BASE + 37
MR_TransformRigTool.REPEAT_TRANSFORMATION = MOHO.MSG_BASE + 38
MR_TransformRigTool.ALT_REPEAT = MOHO.MSG_BASE + 39

function MR_TransformRigTool:DoLayout(moho, layout)
	local fileWord = MOHO.Localize("/Menus/File/File=File")
	
	layout:AddPadding(2)

	self.dlog = MR_SettingsDialog:new()
    self.settingsPopup = LM.GUI.PopupDialog(self:Localize('Settings'), false, 0)
    self.settingsPopup:SetDialog(self.dlog)
    layout:AddChild(self.settingsPopup, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.advancedModeButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_advanced_mode', self:Localize('Advanced Mode'), false, self.ADVANCED_MODE, false)
	layout:AddChild(self.advancedModeButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.fixActionsButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_ok', self:Localize('Finalize'), false, self.FIX_ACTIONS, false)
	layout:AddChild(self.fixActionsButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.clearButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_cancel', self:Localize('Clear'), false, self.CLEAR, false)
	layout:AddChild(self.clearButton, LM.GUI.ALIGN_LEFT, 0)

    layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.centerMenu = LM.GUI.Menu()
	self.centerMenu:AddItem(self:Localize("Custom Center"), 0, self.CUSTOM_CENTER)
	self.centerMenu:AddItem(self:Localize("Selected Bone"), 0, self.SELECTED_BONE)
	
	if fileWord == "Файл" then
		self.centerPopup = LM.GUI.PopupMenu(140, true)
	else	
		self.centerPopup = LM.GUI.PopupMenu(120, true)
	end
	
	self.centerPopup:SetMenu(self.centerMenu)
	layout:AddChild(self.centerPopup)
	
	self.setFromSelectionButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_select_center', self:Localize('Set From Selection'), false, self.SET_FROM_SELECTION, false)
	self.setFromSelectionButton:SetAlternateMessage(self.SET_FROM_SELECTION_ALT)
    layout:AddChild(self.setFromSelectionButton, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	layout:AddPadding(3)
	
	self.duplicateBodypartButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_duplicate_bodypart", self:Localize('Duplicate Bodypart'), false, self.DUPLICATE_BODYPART, false)
	layout:AddChild(self.duplicateBodypartButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.copyBodypartButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_copy_to_another_skeleton", self:Localize('Copy Bodypart'), false, self.COPY_BODYPART, false)
	layout:AddChild(self.copyBodypartButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.flipH = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_flip_points_h", self:Localize('Flip Horizontally'), false, self.FLIP_H, false)
	self.flipH:SetAlternateMessage(self.FLIP_H_ALT)
	layout:AddChild(self.flipH, LM.GUI.ALIGN_LEFT, 0)
	
	self.flipV = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_flip_points_v", self:Localize('Flip Vertically'), false, self.FLIP_V, false)
	self.flipV:SetAlternateMessage(self.FLIP_V_ALT)
	layout:AddChild(self.flipV, LM.GUI.ALIGN_LEFT, 0)
	
	self.duplicateAndFlipSmarboneButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_duplicate_and_flip_smarbone", self:Localize('Duplicate And Flip Smarbone'), false, self.DUPLICATE_AND_FLIP_SMARTBONE, false)
	self.duplicateAndFlipSmarboneButton:SetAlternateMessage(self.DUPLICATE_AND_FLIP_SMARTBONE_ALT)
	layout:AddChild(self.duplicateAndFlipSmarboneButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.reconnectBonesButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_reconnect_bones", self:Localize('Reconnect Bones'), true, self.RECONNECT_BONES, false)
	self.reconnectBonesButton:SetAlternateMessage(self.RECONNECT_BONES_ALT)
	layout:AddChild(self.reconnectBonesButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.selectAllPointsButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_select_all_points", self:Localize('Select All Points'), false, self.SELECT_ALL_POINTS, false)
	self.selectAllPointsButton:SetAlternateMessage(self.DESELECT_ALL_POINTS)
	layout:AddChild(self.selectAllPointsButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.checkForSplitDimensionsButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_split_dimensions", self:Localize('Check For Split Dimensions'), false, self.CHECK_FOR_SPLIT_DIMENSIONS, false)
	layout:AddChild(self.checkForSplitDimensionsButton, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddPadding(1)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	layout:AddPadding(1)
	
	self.ignoreRefCheck = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_ignore_ref', self:Localize('Ignore References'), true, self.IGNORE_REF, false)
    layout:AddChild(self.ignoreRefCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.transformPointsCheck = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_transform_points', self:Localize('Transform Points'), true, self.TRANSFORM_POINTS, false)
    layout:AddChild(self.transformPointsCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.autoSelectCheck = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_auto_select_all_points", self:Localize('Auto Select All Points'), true, self.AUTO_SELECT_POINTS, false)
	layout:AddChild(self.autoSelectCheck, LM.GUI.ALIGN_LEFT, 0)

	self.adjustStrokeWidthCheck = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_stroke_width", self:Localize("Adjust Stroke Width"), true, self.ADJUST_STROKE_WIDTH, false)
	layout:AddChild(self.adjustStrokeWidthCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.followPathAdaptationCheck = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_follow_path", self:Localize("Follow Path Rig Adaptation"), true, self.FOLLOW_PATH_ADAPTATION, false)
	self.followPathAdaptationCheck:SetAlternateMessage(self.FOLLOW_PATH_ADAPTATION_ALT)
	layout:AddChild(self.followPathAdaptationCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.transformBonesCheck = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_selected_bone', self:Localize('Transform Bones'), true, self.TRANSFORM_BONES, false)
    layout:AddChild(self.transformBonesCheck, LM.GUI.ALIGN_LEFT, 0)
	
	if self.isVitruvianBonesAvaible then
		self.transformVitruvianBonesButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_vitruvian_bones', self:Localize('Transform Vitruvian Bones'), true, self.TRANSFORM_VITRUVIAN_BONES, false)
		layout:AddChild(self.transformVitruvianBonesButton, LM.GUI.ALIGN_LEFT, 0)
	end	
	
	self.transformTargetBonesButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_target_bones', self:Localize('Transform Target Bones'), true, self.TRANSFORM_TARGET_BONES, false)
    layout:AddChild(self.transformTargetBonesButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.transformPatchLayerCheck = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_transform_patch', self:Localize('Transform Patch Layers'), true, self.TRANSFORM_PATCH_LAYERS, false)
    layout:AddChild(self.transformPatchLayerCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.transformImageLayersCheck = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_image_layer', self:Localize('Transform Image Layers'), true, self.TRANSFORM_IMAGE_LAYERS, false)
    layout:AddChild(self.transformImageLayersCheck, LM.GUI.ALIGN_LEFT, 0)
	
	self.transformOriginButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_origin', self:Localize('Transform Origins'), true, self.TRANSFORM_ORIGIN, false)
    layout:AddChild(self.transformOriginButton, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddPadding(1)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	layout:AddPadding(1)
	
	self.selectBonesButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_select', self:Localize('Select Bone'), true, self.SELECT_BONE, false)
    layout:AddChild(self.selectBonesButton, LM.GUI.ALIGN_LEFT, 0)

    self.translateButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_translate', self:Localize('Translate'), true, self.TRANSLATE_BONE, false)
    layout:AddChild(self.translateButton, LM.GUI.ALIGN_LEFT, 0)

    self.rotateButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_rotate', self:Localize('Rotate'), true, self.ROTATE_BONE, false)
    layout:AddChild(self.rotateButton, LM.GUI.ALIGN_LEFT, 0)

    self.scaleButton = LM.GUI.ImageButton('ScriptResources/mr_transform_rig_tool/mr_scale', self:Localize('Scale'), true, self.SCALE_BONE, false)
    layout:AddChild(self.scaleButton, LM.GUI.ALIGN_LEFT, 0)
	
	layout:AddPadding(3)
	
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.updateCenterButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_update_center", self:Localize('Update Center'), false, self.UPDATE_CENTER, false)
	self.updateCenterButton:SetAlternateMessage(self.UPDATE_CENTER_ALT)
	layout:AddChild(self.updateCenterButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.repeatLastTransformationButton = LM.GUI.ImageButton("ScriptResources/mr_transform_rig_tool/mr_apply_transformation",self:Localize('Repeat Transformation'), false, self.REPEAT_TRANSFORMATION, false)
	self.repeatLastTransformationButton:SetAlternateMessage(self.ALT_REPEAT)
	layout:AddChild(self.repeatLastTransformationButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.dlog = MR_EditLastTransformDialog:new()
    self.editLastTransformPopup = LM.GUI.PopupDialog(self:Localize('Edit Last Transform'), false, 0)
    self.editLastTransformPopup:SetDialog(self.dlog)
    layout:AddChild(self.editLastTransformPopup, LM.GUI.ALIGN_LEFT, 0)

    layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.statusText = LM.GUI.DynamicText(self:Localize('Status:'), 500)
    layout:AddChild(self.statusText, LM.GUI.ALIGN_LEFT, 0)
end

function MR_TransformRigTool:HandleMessage(moho, view, msg)
	if msg == self.ADVANCED_MODE then
		self:GetBoneTransformation(moho)
	elseif msg == self.FIX_ACTIONS then
		self:ApplyBoneTransformation(moho)
	elseif msg == self.CLEAR then
		self:Cancel(moho)
	elseif (msg >= self.CUSTOM_CENTER and msg <= self.SELECTED_BONE) then
		local center = 0
		if (msg == self.CUSTOM_CENTER) then
			center = 0
		elseif (msg == self.SELECTED_BONE) then
			center = 1
		end	
		self.centerId = center
		self:UpdateWidgets(moho)
	elseif msg == self.SET_FROM_SELECTION then
		self:SetFromSelection(moho)
	elseif msg == self.SET_FROM_SELECTION_ALT then
        self:SetCenterFromBone(moho)
	elseif msg == self.DUPLICATE_BODYPART then
		self:DuplicateBodypart(moho, true)
	elseif msg == self.COPY_BODYPART then
		self:CopyBodypart(moho, true)
	elseif msg == self.FLIP_H then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:Flip(moho, true, false)
	elseif msg == self.FLIP_H_ALT then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:Flip(moho, true, true)
	elseif msg == self.FLIP_V then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:Flip(moho, false, false)
	elseif msg == self.FLIP_V_ALT then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:Flip(moho, false, true)
	elseif msg == self.DUPLICATE_AND_FLIP_SMARTBONE then
		self:DuplicateAndFlipSmarbone(moho)	
	elseif msg == self.DUPLICATE_AND_FLIP_SMARTBONE_ALT then
		self:DuplicateAndFlipSmarbone(moho)
		self:FindSkeletonLayer(moho)
		self:FlipBoneChain(moho, true, false)
	elseif (msg == self.RECONNECT_BONES) then
		moho.document:SetDirty()
		moho.document:PrepUndo(self.skelLayer)
		self:RelinkBones(moho)
		self.areBonesUnlinked = false
		self.reconnectBonesButton:SetValue(self.areBonesUnlinked)
	elseif (msg == self.RECONNECT_BONES_ALT) then
		moho.document:SetDirty()
		moho.document:PrepUndo(self.skelLayer)
		self:AddBonesToList(moho)
		self:UnlinkBones(moho)
		self.areBonesUnlinked = true
		self.reconnectBonesButton:SetValue(self.areBonesUnlinked)
	elseif (msg == self.SELECT_ALL_POINTS) then
		moho.document:PrepUndo(self.skelLayer)
		moho.document:SetDirty()
		local autoSelect = self.autoSelect
		local transformPoints = self.transformPoints
		self.autoSelect = true
		self.transformPoints = true
		self:ScanLayers(moho)
		self.autoSelect = autoSelect
		self.transformPoints = transformPoints
		moho:UpdateUI()	
	elseif (msg == self.DESELECT_ALL_POINTS) then
		moho.document:PrepUndo(self.skelLayer)
		moho.document:SetDirty()
		local autoSelect = self.autoSelect
		local transformPoints = self.transformPoints
		self.autoSelect = false
		self.transformPoints = true
		self:ScanLayers(moho)
		for k, id in ipairs(self.vectorLayersToChange.layer) do
			local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
			local mesh = layer:Mesh()
			if not self.autoSelect then
				mesh:DeselectPoints()
			end
		end	
		self.autoSelect = autoSelect
		self.transformPoints = transformPoints
		moho:UpdateUI()	
	elseif msg == self.CHECK_FOR_SPLIT_DIMENSIONS then
		self:CheckForSplitDimensions(moho)	
	elseif msg == self.IGNORE_REF then
        self.ignoreRefLayers = self.ignoreRefCheck:Value()
	elseif msg == self.TRANSFORM_POINTS then
		self.transformPoints = self.transformPointsCheck:Value()
		self:UpdateWidgets(moho)
	elseif msg == self.AUTO_SELECT_POINTS then
		self.autoSelect = self.autoSelectCheck:Value()
		self:UpdateWidgets(moho)
	elseif msg == self.ADJUST_STROKE_WIDTH then
		self.adjustStrokeWidth = self.adjustStrokeWidthCheck:Value()
	elseif msg == self.FOLLOW_PATH_ADAPTATION then
		self.followPathAdaptation = self.followPathAdaptationCheck:Value()
	elseif msg == self.FOLLOW_PATH_ADAPTATION_ALT then
		moho.document:PrepUndo()
		moho.document:SetDirty()
		moho.layer:SetName(moho.layer:Name())
		moho:UpdateUI()	
	elseif msg == self.TRANSFORM_BONES then
        self.transformBones = self.transformBonesCheck:Value()
		self:UpdateWidgets(moho)
	elseif msg == self.TRANSFORM_VITRUVIAN_BONES then
		self.transformVitruvianBones = self.transformVitruvianBonesButton:Value()	
	elseif msg == self.TRANSFORM_TARGET_BONES then
		self.transformTargetBones = self.transformTargetBonesButton:Value()
	elseif msg == self.TRANSFORM_PATCH_LAYERS then
		self.transformPatchLayers = self.transformPatchLayerCheck:Value()
	elseif msg == self.TRANSFORM_IMAGE_LAYERS then
		self.transformImageLayers = self.transformImageLayersCheck:Value()
	elseif msg == self.TRANSFORM_ORIGIN then
		self.transformOrigin = self.transformOriginButton:Value()	
	elseif msg == self.SELECT_BONE then
		self.selectBonesBt = true
		self.translateBt = false
		self.scaleBt = false
		self.rotateBt = false
		self:UpdateWidgets(moho)
    elseif msg == self.TRANSLATE_BONE then
		self.selectBonesBt = false
		self.translateBt = true
		self.scaleBt = false
		self.rotateBt = false
		self:UpdateWidgets(moho)
    elseif msg == self.SCALE_BONE then
		self.selectBonesBt = false
		self.translateBt = false
		self.scaleBt = true
		self.rotateBt = false
		self:UpdateWidgets(moho)
    elseif msg == self.ROTATE_BONE then
		self.selectBonesBt = false
		self.translateBt = false
		self.scaleBt = false
		self.rotateBt = true
		self:UpdateWidgets(moho)
	elseif msg == self.UPDATE_CENTER then
		self:UpdateLastCenter(moho)
	elseif msg == self.UPDATE_CENTER_ALT then
		self.centerVec:Set(self:GetGlobalPos(moho, moho.layer, moho.layer:Origin()))
		self.lastCenter:Set(self.centerVec)
	elseif msg == self.REPEAT_TRANSFORMATION then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:ScanLayers(moho)
		self.skel = self:FindSkeleton(moho)
		if self.skel == nil then
			return
		end
		local skel = self.skel
		self.mainSkelSelectedBones = self:CountSelectedBones(moho, skel)
		if self.transformPoints then
			self:CollectPointsTempPos(moho)
		end	
		
		for m in pairs(self.fixedActionsList) do
			self.fixedActionsList[m] = nil
		end
		
		if (self.vectorLayersToChange.layer[1] == nil) and self.imageLayersToChange.layer[1] == nil then
			if self.patchLayersToChange.layer[1] == nil then
				if (self.transformBones and self.mainSkelSelectedBones ~= 1) or not self.transformBones then
					self.status = self:Localize('No layers selected for active mode.')
					self:UpdateWidgets(moho)
					return
				end	
			end	
		end
	
		if (self.transformBones and self.mainSkelSelectedBones ~= 1) or (self.centerId == 1 and self.mainSkelSelectedBones ~= 1) then
			self.status = self:Localize('You need to select one bone in this mode.')
			self:UpdateWidgets(moho)
			return
		end
		
		local isMarker = self:CheckMarker(moho)
		
		if not self:RepeatTransformation(moho, false) then
			return
		end
		if self.transformBones and self.mainSkelSelectedBones == 1 then
			if self.transformVitruvianBones and moho.frame == 0 and self.isVitruvianBonesAvaible and not isMarker then
				if self.lastTransformMode == 1 then -- translate
					self:TranslateVitruvianBones(moho, false)
				elseif self.lastTransformMode == 2 then -- scale
					self:ScaleVitruvianBones(moho, false)
				elseif self.lastTransformMode == 5 then -- rotate	
					self:RotateVitruvianBones(moho, false)
				end	
			end	
			
			if self.transformTargetBones and not isMarker then
				if self.lastTransformMode == 1 then -- translate
					self:TransformTargetBones(moho, false)
				elseif self.lastTransformMode == 2 then -- scale
					self:TransformTargetBones(moho, false)
				elseif self.lastTransformMode == 5 then -- rotate	
					self:TransformTargetBones(moho, false)
				end	
			end
		end
		local totalActions = #self.fixedActionsList
		local actionsStr = self:Localize(' actions were updated.')
		if totalActions == 1 then
			actionsStr = self:Localize(' action was updated.')
		end
		if totalActions  > 0 then
			self.status = totalActions.. actionsStr
		else
			self.status = self:Localize('Actions did not need to be updated')
		end
		
		if self.transformPoints then
			self:RefreshCachedLayers(moho)
		end
		
		moho:NewKeyframe(CHANNEL_POINT)
		moho:UpdateUI()
		moho.view:DrawMe()
	elseif msg == self.ALT_REPEAT then
		if not AE_Utilities then
			local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
			self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
			return
		end
		self:ScanLayers(moho)
		self.skel = self:FindSkeleton(moho)
		if self.skel == nil then
			return
		end
		local skel = self.skel
		self.mainSkelSelectedBones = self:CountSelectedBones(moho, skel)
		if self.transformPoints then
			self:CollectPointsTempPos(moho)
		end	
		
		for m in pairs(self.fixedActionsList) do
			self.fixedActionsList[m] = nil
		end
		
		if (self.vectorLayersToChange.layer[1] == nil) and self.imageLayersToChange.layer[1] == nil then
			if self.patchLayersToChange.layer[1] == nil then
				if (self.transformBones and self.mainSkelSelectedBones ~= 1) or not self.transformBones then
					self.status = self:Localize('No layers selected for active mode.')
					self:UpdateWidgets(moho)
					return
				end	
			end	
		end
		
		if (self.transformBones and self.mainSkelSelectedBones ~= 1) or (self.centerId == 1 and self.mainSkelSelectedBones ~= 1) then
			self.status = self:Localize('You need to select one bone in this mode.')
			self:UpdateWidgets(moho)
			return
		end
		
		local isMarker = self:CheckMarker(moho)
		
		if not self:RepeatTransformation(moho, true) then
			return
		end
		if self.transformBones and self.mainSkelSelectedBones == 1 then
			if self.transformVitruvianBones and moho.frame == 0 and self.isVitruvianBonesAvaible and not isMarker then
				if self.lastTransformMode == 1 then -- translate
					self:TranslateVitruvianBones(moho, true)
				elseif self.lastTransformMode == 2 then -- scale
					self:ScaleVitruvianBones(moho, true)
				elseif self.lastTransformMode == 5 then -- rotate	
					self:RotateVitruvianBones(moho, true)
				end	
			end	
			
			if self.transformTargetBones and not isMarker then
				if self.lastTransformMode == 1 then -- translate
					self:TransformTargetBones(moho, true)
				elseif self.lastTransformMode == 2 then -- scale
					self:TransformTargetBones(moho, true)
				elseif self.lastTransformMode == 5 then -- rotate	
					self:TransformTargetBones(moho, true)
				end	
			end
		end
		local totalActions = #self.fixedActionsList
		local actionsStr = self:Localize(' actions were updated.')
		if totalActions == 1 then
			actionsStr = self:Localize(' action was updated.')
		end
		if totalActions  > 0 then
			self.status = totalActions.. actionsStr
		else
			self.status = self:Localize('Actions did not need to be updated')
		end
		
		if self.transformPoints then
			self:RefreshCachedLayers(moho)
		end
		
		moho:NewKeyframe(CHANNEL_POINT)
		moho:UpdateUI()
		moho.view:DrawMe()
	end
end

function MR_TransformRigTool:UpdateWidgets(moho)
	local skel = self:FindSkeleton(moho)
	self:FindSkeletonLayer(moho)
	self.skel = skel
	local isMarker = self:CheckMarker(moho)
	local curLayerId = moho.layer:UUID()
	if self.boneLayerId ~= curLayerId or not isMarker then
		self.fixActionsButton:Enable(false)
	else 
		self.fixActionsButton:Enable(true)
	end
	if moho.frame ~= 0 then
		self.advancedModeButton:Enable(false)
	else
		self.advancedModeButton:Enable(true)
	end
	if isMarker then
		self.clearButton:Enable(true)
		self.advancedModeButton:Enable(false)
		self.scaleButton:Enable(false)
		self.checkForSplitDimensionsButton:Enable(false)
		if self.scaleBt then
			self.scaleBt = false
			self.selectBonesBt = true
		end
	else
		self.clearButton:Enable(false)
		self.scaleButton:Enable(true)
	end	
	if moho.layer:LayerType() ~= MOHO.LT_BONE or moho.document:CurrentDocAction() ~= "" then
		self.advancedModeButton:Enable(false)
		self.fixActionsButton:Enable(false)
	end

	if self.centerId == 0 then
		self.centerMenu:SetChecked(self.CUSTOM_CENTER, true)
		self.centerMenu:SetChecked(self.SELECTED_BONE, false)	
	elseif (self.centerId == 1) then
		self.centerMenu:SetChecked(self.CUSTOM_CENTER, false)
		self.centerMenu:SetChecked(self.SELECTED_BONE, true)	
	end
	self.centerPopup:Redraw()
	
	self.reconnectBonesButton:SetValue(self.areBonesUnlinked)
	self.ignoreRefCheck:SetValue(self.ignoreRefLayers)
	self.transformPointsCheck:SetValue(self.transformPoints)
	self.autoSelectCheck:SetValue(self.autoSelect)
	self.autoSelectCheck:Enable(self.transformPoints)
	self.adjustStrokeWidthCheck:SetValue(self.adjustStrokeWidth)
	self.adjustStrokeWidthCheck:Enable(self.transformPoints)
	self.followPathAdaptationCheck:SetValue(self.followPathAdaptation)
	self.followPathAdaptationCheck:Enable(self.transformPoints)
	self.transformBonesCheck:SetValue(self.transformBones)
	self.transformBonesCheck:Enable(true)
	if self.isVitruvianBonesAvaible then
		self.transformVitruvianBonesButton:SetValue(self.transformVitruvianBones)
		self.transformVitruvianBonesButton:Enable(self.transformBones and not isMarker)
	end	
	self.transformTargetBonesButton:SetValue(self.transformTargetBones)
	self.transformTargetBonesButton:Enable(self.transformBones and not isMarker)
	self.transformPatchLayerCheck:SetValue(self.transformPatchLayers)
	self.transformImageLayersCheck:SetValue(self.transformImageLayers)
	self.transformOriginButton:SetValue(self.transformOrigin)
	
	self.selectBonesButton:SetValue(self.selectBonesBt)
    self.translateButton:SetValue(self.translateBt)
    self.scaleButton:SetValue(self.scaleBt)
    self.rotateButton:SetValue(self.rotateBt)
    
	if self.isSkel and moho.frame == 0 then		
		if (self.selectBonesBt) then
			moho.view:SetCursor(LM.GUI.Cursor("ScriptResources/mr_transform_rig_tool/mr_cursor", 1, 1))
			self.editLastTransformPopup:Enable(false)
		elseif (self.translateBt) then
			moho.view:SetCursor(MOHO.moveCursor)
			self.editLastTransformPopup:Enable(true)
		elseif (self.scaleBt) then
			moho.view:SetCursor(MOHO.scaleCursor)
			self.editLastTransformPopup:Enable(true)
		elseif (self.rotateBt) then
			moho.view:SetCursor(MOHO.rotateCursor)
			self.editLastTransformPopup:Enable(true)
		end
	else	
		moho.view:SetCursor(MOHO.disabledCursor)
		self.editLastTransformPopup:Enable(false)
	end	
		
	if moho.frame ~= 0 then
		moho.view:SetCursor(MOHO.disabledCursor)
	end	
	
	if self.lastTransformMode == 0 then
		self.repeatLastTransformationButton:Enable(false)
	elseif self.lastTransformMode == 1 and self.translateBt then
		self.repeatLastTransformationButton:SetToolTip(self:Localize('Repeat Translate'))	
		self.updateCenterButton:Enable(true)
		self.repeatLastTransformationButton:Enable(true)
	elseif self.lastTransformMode == 2 and self.scaleBt then
		self.repeatLastTransformationButton:SetToolTip(self:Localize('Repeat Scale'))
		self.updateCenterButton:Enable(true)		
		self.repeatLastTransformationButton:Enable(true)
	elseif self.lastTransformMode >= 3 and self.lastTransformMode <= 4 and self.scaleBt then
		self.repeatLastTransformationButton:SetToolTip(self:Localize('Repeat Scale'))	
		self.updateCenterButton:Enable(true)
		self.repeatLastTransformationButton:Enable(true)
	elseif self.lastTransformMode == 5 and self.rotateBt then
		self.repeatLastTransformationButton:SetToolTip(self:Localize('Repeat Rotate'))
		self.updateCenterButton:Enable(true)		
		self.repeatLastTransformationButton:Enable(true)
	else	
		self.repeatLastTransformationButton:Enable(false)
	end
	
	if moho.frame == 0 and moho.layer:CurrentAction() == '' then
		self.duplicateBodypartButton:Enable(not isMarker)
		self.copyBodypartButton:Enable(not isMarker)
		self.flipH:Enable(not isMarker)
		self.flipV:Enable(not isMarker)
		self.duplicateAndFlipSmarboneButton:Enable(not isMarker)
		self.selectAllPointsButton:Enable(not isMarker)
		self.editLastTransformPopup:Enable(not self.selectBonesBt)
		if self.centerId == 0 then
			self.setFromSelectionButton:Enable(true)
		else
			self.setFromSelectionButton:Enable(false)
		end
	else
		self.centerPopup:Enable(false)
		self.setFromSelectionButton:Enable(false)
		self.duplicateBodypartButton:Enable(false)
		self.copyBodypartButton:Enable(false)
		self.flipH:Enable(false)
		self.flipV:Enable(false)
		self.duplicateAndFlipSmarboneButton:Enable(false)
		self.reconnectBonesButton:Enable(false)
		self.selectAllPointsButton:Enable(false)
		self.checkForSplitDimensionsButton:Enable(false)
		self.selectBonesButton:Enable(false)
		self.translateButton:Enable(false)
		self.rotateButton:Enable(false)
		self.scaleButton:Enable(false)
		self.updateCenterButton:Enable(false)
		self.repeatLastTransformationButton:Enable(false)
		self.editLastTransformPopup:Enable(false)
	end
	self.statusText:SetValue(self.status)
end

function MR_TransformRigTool:Cancel(moho)
	moho.document:PrepUndo(moho.layer)
	moho.document:SetDirty()
	local markerChannels = moho.layer.fTimelineMarkers
	local framesToDel = {}
	local isNeedUpdate = false
	if markerChannels:Duration() > 0 then
		for i=1, markerChannels:CountKeys()-1 do
			local markerTime = markerChannels:GetKeyWhen(i)
			local markerText = markerChannels:GetValue(markerTime)
			if markerText == 'Do not delete or edit!' then
				self.refKey = markerTime
				table.insert(framesToDel, markerTime)
				isNeedUpdate = true
			end
		end
		for y in pairs(framesToDel) do
			markerChannels:DeleteKey(framesToDel[y])
			self:ClearFrame(moho, framesToDel[y])
			if framesToDel[y] > 1 then
				self:ClearFrame(moho, framesToDel[y]-1)
			end
		end
		self.status = self:Localize('Collected data cleared.')
		self:UpdateWidgets(moho)	
		if isNeedUpdate then
			self:HardUpdate(moho)
		end
	end
end

function MR_TransformRigTool:CheckMarker(moho)
	local found = false
	local markerChannels
	if self.skelLayer then
		markerChannels = self.skelLayer.fTimelineMarkers
	else
		markerChannels = moho.layer.fTimelineMarkers
	end
	if markerChannels:Duration() > 0 then
		for i=1, markerChannels:CountKeys()-1 do
			local markerTime = markerChannels:GetKeyWhen(i)
			local markerText = markerChannels:GetValue(markerTime)
			if markerText == 'Do not delete or edit!' then
				found = true
				break
			end
		end	
	end		
	return found
end

function MR_TransformRigTool:FindLastKey(moho)
	local lastKey = 0
	local skel = moho:Skeleton()
	for i=0, skel:CountBones()-1 do 
		local myBone = skel:Bone(i)
		local channelAngle = myBone.fAnimAngle
		local channelPos = myBone.fAnimPos
		local channelScale = myBone.fAnimScale
		if channelAngle:Duration() > lastKey then
			lastKey = channelAngle:Duration()
		end
		if channelPos:Duration() > lastKey then
			lastKey = channelPos:Duration()
		end
		if channelScale:Duration() > lastKey then
			lastKey = channelScale:Duration()
		end
	end
	return lastKey
end

function MR_TransformRigTool:RejoinDimensionsInSelectedBones(moho)
	local skel = moho:Skeleton()
	local isAllowRejoin = false
	local isSuccessful = true
	local isIgnoreMainLine = false
	for i=0, skel:CountBones()-1 do 
		local myBone = skel:Bone(i)
		if myBone.fSelected then
			for act=0, myBone.fAnimPos:CountActions()-1 do
				local actName = myBone.fAnimPos:ActionName(act)
				local isSmartBone = moho.layer:IsSmartBoneAction(actName)
				if isSmartBone then
					local actionChannel = moho:ChannelAsAnimVec2(myBone.fAnimPos:Action(act))
					if actionChannel then
						if actionChannel:AreDimensionsSplit() then
							if not isAllowRejoin then
								local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Channels with separated dimensions were found.'),
								self:Localize('You can Rejoin Dimensions to continue or cancel operation.'), "",  self:Localize('Rejoin Dimensions'), self:Localize('Cancel'),"")
								if ans == 0 then
									isAllowRejoin = true
								elseif ans == 1 then
									isSuccessful = false
									return isSuccessful
								end	
							end	
							if isAllowRejoin then
								actionChannel:SplitDimensions(false)
							end	
						end
					end
				end	
			end
		end
	end
	return isSuccessful
end

function MR_TransformRigTool:GetBoneTransformation(moho)
	for k in pairs(self.boneList) do
		self.boneList[k] = nil
		self.boneOrigPosList[k] = nil
		self.boneOrigGlobalPosList[k] = nil
		self.boneAngleList[k] = nil
		self.boneAngleExtraList[k] = nil
		self.boneAngleOffsetList[k] = nil
		self.boneParentAngleList[k] = nil
		self.boneParentList[k] = nil
		self.boneParentMatrixList[k] = nil
	end
	
	for k in pairs(self.boneRefList) do
		self.boneRefList[k] = nil
	end
	
	local skel = moho:Skeleton()
	
	self.totalBones = 0
	local bonesFound = moho:CountSelectedBones(true)
	
	if bonesFound > 0 then
		moho.document:PrepUndo(moho.layer)
		moho.document:SetDirty()
		
		local curLayerId = moho.layer:UUID()
		if self.boneLayerId ~= curLayerId then
			self.refKey = 1
		end
		
		local markerChannels = moho.layer.fTimelineMarkers
		local framesToDel = {}
		local isNeedUpdate = false
		if markerChannels:Duration() > 0 then
			for i=1, markerChannels:CountKeys()-1 do
				local markerTime = markerChannels:GetKeyWhen(i)
				local markerText = markerChannels:GetValue(markerTime)
				if markerText == 'Do not delete or edit!' then
					self.refKey = markerTime
					table.insert(framesToDel, markerTime)
					isNeedUpdate = true
				end
			end
			for y in pairs(framesToDel) do
				markerChannels:DeleteKey(framesToDel[y])
				self:ClearFrame(moho, framesToDel[y])
				if framesToDel[y] > 1 then
					self:ClearFrame(moho, framesToDel[y]-1)
				end
			end	
			if isNeedUpdate then
				self:HardUpdate(moho)
			end
		end

		local lastFrame = self:FindLastKey(moho)
		
		if lastFrame >= 1 then
			self.refKey = lastFrame + 2
		else
			self.refKey = 1
		end
		
		for i=0, skel:CountBones()-1 do 
			table.insert(self.boneRefList,i)
		end
		
		if self.refKey > 1 then
			self:SetKey(moho, self.refKey -1, self.refKey -1)
		end
		self:SetKey(moho, self.refKey, 0)
		local keyInterp = MOHO.InterpSetting:new_local()
		keyInterp.tags = 1
		moho.layer.fTimelineMarkers:SetValue(self.refKey,'Do not delete or edit!')
		moho.layer.fTimelineMarkers:SetKeyInterp(self.refKey, keyInterp)
	end
	
	local isSuccessful = self:RejoinDimensionsInSelectedBones(moho)
	if not isSuccessful then
		self.status = self:Localize('Canceled.')
		self:UpdateWidgets(moho)
		self:ClearFrame(moho, self.refKey)
	
		if self.refKey > 1 then
			self:ClearFrame(moho, self.refKey-1)
		end
		self:HardUpdate(moho)
		return
	end
	
	for i=0, skel:CountBones()-1 do 
		local myBone = skel:Bone(i)
		self.totalBones = self.totalBones + 1
		if myBone.fSelected then
			table.insert(self.boneList, i)
			local parentBone = skel:Bone(myBone.fParent)
			
			if myBone.fParent >= 0 then
				local parentMatrix = LM.Matrix:new_local()
				parentMatrix:Set(parentBone.fRestMatrix)
				table.insert(self.boneParentList, myBone.fParent)
				table.insert(self.boneParentMatrixList, parentMatrix)
			else
				local emtyMatrix = LM.Matrix:new_local()
				emtyMatrix:Identity()
				table.insert(self.boneParentMatrixList, emtyMatrix)
				table.insert(self.boneParentList, -1)
			end
			
			local bonePos = LM.Vector2:new_local()		
			bonePos = myBone.fAnimPos:GetValue(0)
			local boneAngle = myBone.fAnimAngle:GetValue(0)
			local boneAngleExtra = myBone.fAnimAngle:GetValue(self.refKey)
			table.insert(self.boneOrigPosList, myBone.fAnimPos:GetValue(0))
			local boneParentAngle = 0
				
			if myBone.fParent >= 0 then
				boneParentAngle = parentBone.fAnimAngle:GetValue(0)
				parentBone.fRestMatrix:Transform(bonePos)
			end
			
			table.insert(self.boneOrigGlobalPosList, bonePos)
			table.insert(self.boneAngleList, boneAngle)
			table.insert(self.boneAngleExtraList, boneAngleExtra)
			table.insert(self.boneParentAngleList, boneParentAngle)
		end	
	end
	
	local layerName = moho.layer:Name()
	
	if bonesFound > 0 then
		self.boneLayerId = moho.layer:UUID()
		if bonesFound == 1 then
			self.status = self:Localize('Original transformation collected for 1 bone on layer ') .. layerName..'.'
		else
			self.status = self:Localize('Original transformation collected for ') .. bonesFound.. self:Localize(' bones on layer ') .. layerName..'.'
		end	
	else
		self.boneLayerId = nil
		self.status = self:Localize('No bones was selected. Please select bones first.')
	end
	
	moho.view:DrawMe()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
	self:UpdateWidgets(moho)
end

function MR_TransformRigTool:SwapTwoArrayKeys(array, key1, key2)
	local keyTmp1 = array[key1]
	local keyTmp2 = array[key2]
	return keyTmp2, keyTmp1
end

function MR_TransformRigTool:ApplyBoneTransformation(moho)
	self:UpdateWidgets(moho)
	
	for q in pairs(self.fixedBonesList) do
		self.fixedBonesList[q] = nil
	end

	for m in pairs(self.fixedActionsList) do
		self.fixedActionsList[m] = nil
	end
	
	self.fixedBones = 0
	self.fixedActions = 0
	
	local curFrame = moho.frame
	local skel = moho:Skeleton()
	
	if (skel == nil) then
		return
	elseif (self.boneList[1] == nil) then
		return
	end
	
	for b in pairs(self.boneList) do
		local bone = skel:Bone(self.boneList[b])
		if bone == nil then
			self.status = self:Localize('Correction was canceled.')
			self:UpdateWidgets(moho)
			moho.document:PrepUndo(moho.layer)
			moho.document:SetDirty()
			self:ClearFrame(moho, self.refKey)
			if self.refKey > 1 then
				self:ClearFrame(moho, self.refKey-1)
			end
			local markerChannels = moho.layer.fTimelineMarkers
			markerChannels:DeleteKey(self.refKey)
			self:HardUpdate(moho)
			return LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Some bones are missing. The skeleton structure was changed.'), "", "", self:Localize('EXIT'))
		end
	end
		
	local totalCurBones = 0
	for i=0, skel:CountBones()-1 do 
		totalCurBones = totalCurBones + 1
		local myBone = skel:Bone(i)
		local channelAngle = myBone.fAnimAngle
		local channelPos = myBone.fAnimPos
		local channelScale = myBone.fAnimScale
		local isRefKeysOk = true
		
		local isPosChannelSplit = channelPos:AreDimensionsSplit()
		if isPosChannelSplit then
			local subChannel1 = channelPos:DimensionChannel(0)
			local subChannel2 = channelPos:DimensionChannel(1)
			if not subChannel1:HasKey(self.refKey) or not subChannel2:HasKey(self.refKey) then
				isRefKeysOk = false
			end
		elseif not channelPos:HasKey(self.refKey) then
			isRefKeysOk = false
		end
		if not channelAngle:HasKey(self.refKey) or not channelScale:HasKey(self.refKey) then
			isRefKeysOk = false
		end
		
		if not isRefKeysOk and self.boneRefList[i + 1] ~= nil then
			self.status = self:Localize('Correction was canceled.')
			self:UpdateWidgets(moho)
			moho.document:PrepUndo(moho.layer)
			moho.document:SetDirty()
			self:ClearFrame(moho, self.refKey)
			if self.refKey > 1 then
				self:ClearFrame(moho, self.refKey-1)
			end
			local markerChannels = moho.layer.fTimelineMarkers
			markerChannels:DeleteKey(self.refKey)
			self:HardUpdate(moho)
			return LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Some reference keys are missing. Please do not remove or edit any reference keys.'), "", "", self:Localize('EXIT'))
		end
	end
	
	if totalCurBones < self.totalBones then
		self.status = self:Localize('Correction was canceled.')
		self:UpdateWidgets(moho)
		moho.document:PrepUndo(moho.layer)
		moho.document:SetDirty()
		self:ClearFrame(moho, self.refKey)
		if self.refKey > 1 then
			self:ClearFrame(moho, self.refKey-1)
		end
		local markerChannels = moho.layer.fTimelineMarkers
		markerChannels:DeleteKey(self.refKey)
		self:HardUpdate(moho)
		return LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('The original number of bones is different.'), "", "", self:Localize('EXIT'))
	end
	
	local isChanges = false
	
	for r in pairs(self.boneList) do
		local myBone = skel:Bone(self.boneList[r])
		local extraBoneAngle = self.boneAngleExtraList[r]
		local extraBoneAngleTmp = myBone.fAnimAngle:GetValue(self.refKey)
		if myBone.fParent > -1 then
			local origParentMatrix = LM.Matrix:new_local()
			origParentMatrix:Set(self.boneParentMatrixList[r])
			extraBoneAngle = self:GetGlobalBoneAngle(moho, origParentMatrix, extraBoneAngle, 0, false)
			extraBoneAngleTmp = self:GetGlobalBoneAngle(moho, origParentMatrix, extraBoneAngleTmp, self.refKey, false)
		end	
		local reparentOffset = extraBoneAngle - extraBoneAngleTmp
		table.insert(self.boneAngleOffsetList, reparentOffset)
	end
	
	repeat
		isChanges = false
		for y in pairs(self.boneList) do
			local myBone = skel:Bone(self.boneList[y])
			if myBone.fParent > -1 then
				local newParentId = myBone.fParent
				for t in pairs(self.boneList) do
					if newParentId == self.boneList[t] then
						if y < t then
							isChanges = true
							local key2 = self.boneList[t]
							self.boneList[y], self.boneList[t] = self:SwapTwoArrayKeys(self.boneList, y, t)
							self.boneOrigPosList[y], self.boneOrigPosList[t] = self:SwapTwoArrayKeys(self.boneOrigPosList, y, t)
							self.boneOrigGlobalPosList[y], self.boneOrigGlobalPosList[t] = self:SwapTwoArrayKeys(self.boneOrigGlobalPosList, y, t)
							self.boneAngleList[y], self.boneAngleList[t] = self:SwapTwoArrayKeys(self.boneAngleList, y, t)
							self.boneAngleExtraList[y], self.boneAngleExtraList[t] = self:SwapTwoArrayKeys(self.boneAngleExtraList, y, t)
							self.boneAngleOffsetList[y], self.boneAngleOffsetList[t] = self:SwapTwoArrayKeys(self.boneAngleOffsetList, y, t)
							self.boneParentAngleList[y], self.boneParentAngleList[t] = self:SwapTwoArrayKeys(self.boneParentAngleList, y, t)
							self.boneParentList[y], self.boneParentList[t] = self:SwapTwoArrayKeys(self.boneParentList, y, t)
							self.boneParentMatrixList[y], self.boneParentMatrixList[t] = self:SwapTwoArrayKeys(self.boneParentMatrixList, y, t)
							break
						end	
					else
						local nextBone = skel:Bone(newParentId)
						repeat 
							local prevBone = nextBone
							for g in pairs(self.boneList) do
								if prevBone.fParent == self.boneList[g] then
									if y < g then
										isChanges = true
										local key2 = self.boneList[g]
										self.boneList[y], self.boneList[g] = self:SwapTwoArrayKeys(self.boneList, y, g)
										self.boneOrigPosList[y], self.boneOrigPosList[g] = self:SwapTwoArrayKeys(self.boneOrigPosList, y, g)
										self.boneOrigGlobalPosList[y], self.boneOrigGlobalPosList[g] = self:SwapTwoArrayKeys(self.boneOrigGlobalPosList, y, g)
										self.boneAngleList[y], self.boneAngleList[g] = self:SwapTwoArrayKeys(self.boneAngleList, y, g)
										self.boneAngleExtraList[y], self.boneAngleExtraList[g] = self:SwapTwoArrayKeys(self.boneAngleExtraList, y, g)
										self.boneAngleOffsetList[y], self.boneAngleOffsetList[g] = self:SwapTwoArrayKeys(self.boneAngleOffsetList, y, g)
										self.boneParentAngleList[y], self.boneParentAngleList[g] = self:SwapTwoArrayKeys(self.boneParentAngleList, y, g)
										self.boneParentList[y], self.boneParentList[g] = self:SwapTwoArrayKeys(self.boneParentList, y, g)
										self.boneParentMatrixList[y], self.boneParentMatrixList[g] = self:SwapTwoArrayKeys(self.boneParentMatrixList, y, g)
									end	
								end		
							end
							if nextBone.fParent > -1 then
								nextBone = skel:Bone(nextBone.fParent)
							end
						until nextBone == prevBone
					end
				end
			end	
		end	
	until isChanges == false
		
	moho.document:PrepUndo(moho.layer)
	moho.document:SetDirty()
	
	for i in pairs(self.boneList) do
		local myBone = skel:Bone(self.boneList[i])
		for act=0, moho.layer:CountActions()-1 do
			local actName = moho.layer:ActionName(act)
			local actionChannel = myBone.fAnimAngle:ActionByName(actName)
			local found = false
			local isSmartBone = moho.layer:IsSmartBoneAction(actName)
			
			if actionChannel and actionChannel:Duration()>0 and isSmartBone then
				self:ScanAction(moho, skel, myBone, actName, i)
				found = true
			end
			if found == false and isSmartBone then
				actionChannel = myBone.fAnimPos:ActionByName(actName)
				if actionChannel and actionChannel:Duration()>0 then 
					self:ScanAction(moho, skel, myBone, actName, i)
					found = true
				end
			end
			
			if found == true then
				self.fixedActions = self.fixedActions + 1
			end	
		end
	end
	
	moho.layer:ActivateAction(nil)
	
	self:ClearFrame(moho, self.refKey)
	local markerChannels = moho.layer.fTimelineMarkers
	markerChannels:DeleteKey(self.refKey)
	if self.refKey > 1 then
		self:ClearFrame(moho, self.refKey-1)
	end
	
	local totalBones = #self.fixedBonesList
	local totalActions = #self.fixedActionsList
	local actionsStr = self:Localize(' actions.')
	local bonesStr = self:Localize(' bones were')
	
	if totalBones == 1 then
		bonesStr = self:Localize(' bone was')
	end
	if totalActions == 1 then
		actionsStr = self:Localize(' action.')
	end
	
	self.status = self:Localize('Correction completed! ')..totalBones.. bonesStr..self:Localize(' fixed in ').. totalActions.. actionsStr
	
	if totalBones == 0 or totalActions == 0 then
		self.status = self:Localize('Could not find any actions that need to be fixed.')
	end
	
	self:UpdateWidgets(moho)
	moho:SetCurFrame(curFrame)
	self:HardUpdate(moho)
end

function MR_TransformRigTool:HardUpdate(moho)
	moho:SetCurFrame(1)
	moho:SetCurFrame(0)
	moho:UpdateSelectedChannels()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
end

function MR_TransformRigTool:ClearFrame(moho, frame)
	local skel = moho:Skeleton()
	for i=0, skel:CountBones()-1 do 
		local myBone = skel:Bone(i)
		myBone.fAnimPos:DeleteKey(frame)
		myBone.fAnimAngle:DeleteKey(frame)
		myBone.fAnimScale:DeleteKey(frame)
	end
end

function MR_TransformRigTool:SetKey(moho, frame, valueFromFrame)
	local keyInterp = MOHO.InterpSetting:new_local()
	keyInterp.tags = 1
	local skel = moho:Skeleton()
	for i=0, skel:CountBones()-1 do 
		local myBone = skel:Bone(i)
		local channelPos = myBone.fAnimPos
		local pos = channelPos:GetValue(valueFromFrame)
		local angle = myBone.fAnimAngle:GetValue(valueFromFrame)
		local scale = myBone.fAnimScale:GetValue(valueFromFrame)
		local isPosChannelSplit = channelPos:AreDimensionsSplit()
		if isPosChannelSplit then
			local subChannel1 = channelPos:DimensionChannel(0)
			local subChannel2 = channelPos:DimensionChannel(1)
			if subChannel1 then
				subChannel1:SetValue(frame, pos.x)
				subChannel1:SetKeyInterp(frame, keyInterp)
			end	
			if subChannel2 then
				subChannel2:SetValue(frame, pos.y)
				subChannel2:SetKeyInterp(frame, keyInterp)
			end
		else	
			myBone.fAnimPos:SetValue(frame, pos)
			myBone.fAnimPos:SetKeyInterp(frame, keyInterp)
		end
		myBone.fAnimAngle:SetValue(frame, angle)
		myBone.fAnimAngle:SetKeyInterp(frame, keyInterp)
		if valueFromFrame == 0 then
			myBone.fAnimScale:SetValue(frame, 1)
			myBone.fAnimScale:SetKeyInterp(frame, keyInterp)
		else
			myBone.fAnimScale:SetValue(frame, scale)
			myBone.fAnimScale:SetKeyInterp(frame, keyInterp)
		end
	end
end

function MR_TransformRigTool:ScanAction(moho, skel, bone, actionName, boneNum)
	moho.layer:ActivateAction(actionName)

	local actionChannelPos =  bone.fAnimPos:ActionByName(actionName)
	local actionChannelAngle =  bone.fAnimAngle:ActionByName(actionName)

	for i=1, actionChannelPos:CountKeys()-1 do
		local keyTime = actionChannelPos:GetKeyWhen(i)
		if keyTime > 0 then
			self:ApplyPosition(moho, skel, bone, keyTime, boneNum, actionName)
		end
	end
	for i=1, actionChannelAngle:CountKeys()-1 do
		local keyTime = actionChannelAngle:GetKeyWhen(i)
		if keyTime > 0 then
			self:ApplyAngle(moho, skel, bone, keyTime, boneNum, actionName)
		end	
	end
end

function MR_TransformRigTool:ApplyPosition(moho, skel, bone, frame, boneNum, actionName)
	moho:SetCurFrame(frame)
	local myBone = bone
	local parentBone = myBone.fParent
	local oldParentBone = self.boneParentList[boneNum]
	local localOrigBonePos = LM.Vector2:new_local()
	localOrigBonePos:Set(self.boneOrigPosList[boneNum])
	local origBoneGlobalPos = LM.Vector2:new_local()
	origBoneGlobalPos:Set(self.boneOrigGlobalPosList[boneNum])
	local bonePosFrameZero = LM.Vector2:new_local()	
	local oldMatrix = LM.Matrix:new_local()
	oldMatrix:Set(self.boneParentMatrixList[boneNum])
	local parent = skel:Bone(parentBone)
	local parentOffset = LM.Vector2:new_local()
	local parentOffsetZero = LM.Vector2:new_local()
	parentOffset:Set(0,0)
	parentOffsetZero:Set(0,0)
	
	if parent ~= nil then
		parent.fMovedMatrix:Transform(parentOffset)  
		parent.fRestMatrix:Transform(parentOffsetZero)
	end

	parentOffset = parentOffsetZero - parentOffset

	bonePosFrameZero:Set(myBone.fAnimPos:GetValue(0))
	local oldParent = skel:Bone(oldParentBone)
	
	if parent ~= nil then
		parent.fRestMatrix:Transform(bonePosFrameZero)  
	end
	
	local bonePos = LM.Vector2:new_local()	
	bonePos:Set(myBone.fAnimPos:GetValue(frame))
	
	local bonePosDifFrameZero = LM.Vector2:new_local()
	bonePosDifFrameZero = origBoneGlobalPos - bonePosFrameZero
	local localBonePosFrameZero = LM.Vector2:new_local()	
	localBonePosFrameZero:Set(myBone.fAnimPos:GetValue(0))
	oldMatrix:Transform(bonePos)   
	
	bonePos = bonePos - bonePosDifFrameZero  - parentOffset

	if parent ~= nil then
		local inverseM = LM.Matrix:new_local()
		inverseM:Set(parent.fMovedMatrix) 
		inverseM:Invert()
		inverseM:Transform(bonePos)
	end
	
	if localOrigBonePos.x ~= localBonePosFrameZero.x
	or localOrigBonePos.y ~= localBonePosFrameZero.y
	or oldParentBone ~= parentBone then
		myBone.fAnimPos:SetValue(frame, bonePos)
		self:CollectLog(myBone:Name(), actionName)
	end
end

function MR_TransformRigTool:CollectLog(boneName, actionName)
	local isBoneNew = true
	local isActionNew = true
	
	if boneName ~= nil then
		for z in pairs(self.fixedBonesList) do
			if self.fixedBonesList[z] == boneName then
				isBoneNew = false
				break
			end	
		end
		if isBoneNew then
			table.insert(self.fixedBonesList, boneName)
		end
	end
	if actionName ~= '' and actionName ~= nil then
		for a in pairs(self.fixedActionsList) do
			if self.fixedActionsList[a] == actionName then
				isActionNew = false
				break
			end	
		end
		if isActionNew then
			table.insert(self.fixedActionsList, actionName)
		end
	end
end

function MR_TransformRigTool:GetGlobalBoneAngle(moho, matrix, boneAngle, frame, round)
	local v1 = LM.Vector2:new_local()
	local v2 = LM.Vector2:new_local()
	v1:Set(0,0)
	v2:Set(1,0)
	local newAngle = 0
	local invMatrix = LM.Matrix:new_local()	
										
	invMatrix:Set(matrix)
	invMatrix:Invert()
	invMatrix:Transform(v1)
	invMatrix:Transform(v2)
	v2 = v2-v1
	newAngle = math.atan2(v2.y, v2.x)
	
	if round == true then
		while newAngle > 2 * math.pi do
			newAngle = newAngle - 2 * math.pi
		end
		while newAngle < - 2 * math.pi do
			newAngle = newAngle + 2 * math.pi
		end	
	end
	newAngle = boneAngle - newAngle
	return newAngle
end

function MR_TransformRigTool:ApplyAngle(moho, skel, bone, frame, boneNum, actionName)
	moho:SetCurFrame(frame)
	local myBone = bone
	local oldBoneAngle = self.boneAngleList[boneNum]
	local newBoneAngle = myBone.fAnimAngle:GetValue(frame)
	local newBoneAngleFrameZero = myBone.fAnimAngle:GetValue(0)
	local newParentBoneId = myBone.fParent
	local oldParentBoneId = self.boneParentList[boneNum]
	local boneAngleDifFrameZero = 0
	local parentAngleOffset = 0
	local oldParentAngleOffset = 0
	
	if oldBoneAngle ~= newBoneAngleFrameZero and oldParentBoneId == newParentBoneId then
		local oldParentBone = skel:Bone(oldParentBoneId)
		boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero
		newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + self.boneAngleOffsetList[boneNum]
		self:CollectLog(myBone:Name(), actionName) 
	elseif oldBoneAngle ~= newBoneAngleFrameZero and oldParentBoneId ~= newParentBoneId and oldParentBoneId >= 0 then
		local oldParentBone = skel:Bone(oldParentBoneId)
		local oldMatrix = LM.Matrix:new_local()
		oldMatrix = oldParentBone.fMovedMatrix
		
		if newParentBoneId > -1 then
			local parentBone = skel:Bone(newParentBoneId)
			if self.considerNewParentRotation then
				local parentAngleZero = parentBone.fAnimAngle:GetValue(0)
				local parentAngle = parentBone.fAnimAngle:GetValue(frame)
				if	parentBone.fParent > -1 then
					local grandPaBone = skel:Bone(parentBone.fParent)
					parentAngleZero = self:GetGlobalBoneAngle(moho, grandPaBone.fRestMatrix, parentAngleZero, 0, false)
					parentAngle = self:GetGlobalBoneAngle(moho, grandPaBone.fMovedMatrix, parentAngle, frame, false)
				end
				parentAngleOffset = parentAngleZero - parentAngle
			end
		end	
		oldBoneAngle = oldBoneAngle - self.boneAngleOffsetList[boneNum]
		
		if oldParentBoneId > -1 then
			local oldParentBone = skel:Bone(oldParentBoneId)
			if self.considerOldParentRotation then
				local oldParentAngleZero = oldParentBone.fAnimAngle:GetValue(0)
				local oldParentAngle = oldParentBone.fAnimAngle:GetValue(frame)
				if	oldParentBone.fParent > -1 then
					local oldGandPaBone = skel:Bone(oldParentBone.fParent)
					oldParentAngleZero = self:GetGlobalBoneAngle(moho, oldGandPaBone.fRestMatrix, oldParentAngleZero, 0, false)
					oldParentAngle = self:GetGlobalBoneAngle(moho, oldGandPaBone.fMovedMatrix, oldParentAngle, frame, false)
				end
				oldParentAngleOffset = oldParentAngleZero - oldParentAngle
			end
		end
		boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero
		newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + parentAngleOffset - oldParentAngleOffset
		self:CollectLog(myBone:Name(), actionName)
	elseif newParentBoneId > -1 and oldParentBoneId < 0 then
		local parentBone = skel:Bone(newParentBoneId)
		local newMatrix = LM.Matrix:new_local()
		newMatrix = parentBone.fMovedMatrix

		if self.considerNewParentRotation then
			local parentAngleZero = parentBone.fAnimAngle:GetValue(0)
			local parentAngle = parentBone.fAnimAngle:GetValue(frame)
			if	parentBone.fParent > -1 then
			local grandPaBone = skel:Bone(parentBone.fParent)
				parentAngleZero = self:GetGlobalBoneAngle(moho, grandPaBone.fRestMatrix, parentAngleZero, 0, false)
				parentAngle = self:GetGlobalBoneAngle(moho, grandPaBone.fMovedMatrix, parentAngle, frame, false)
			end	
			parentAngleOffset = parentAngleZero - parentAngle
		end	
		newBoneAngleFrameZero = newBoneAngleFrameZero + self.boneAngleOffsetList[boneNum]
		boneAngleDifFrameZero = oldBoneAngle - newBoneAngleFrameZero
		newBoneAngle = (newBoneAngle - boneAngleDifFrameZero) + parentAngleOffset 
		self:CollectLog(myBone:Name(), actionName)
	end
	myBone.fAnimAngle:SetValue(frame,newBoneAngle)
end

function MR_TransformRigTool:DrawMe(moho, view)
	if not self.isSkel or moho.frame ~= 0 then
		return
	end

	local g = view:Graphics()
	local min = LM.Vector2:new_local()
	local max = LM.Vector2:new_local()
	local matrix = LM.Matrix:new_local()
	local centerVec = LM.Vector2:new_local()
	local v = LM.Vector2:new_local()
	local vc1 = LM.ColorVector:new_local()
	local vc2 = LM.ColorVector:new_local()
	local vc3 = LM.ColorVector:new_local()
	vc1:Set(MOHO.MohoGlobals.SelCol)
	
	local blackColorRGB = LM.rgb_color:new_local()
	blackColorRGB.r = 0
	blackColorRGB.g = 0
	blackColorRGB.b = 0
	blackColorRGB.a = 255
	vc2:Set(MOHO.MohoGlobals.BackCol)
	vc3:Set(blackColorRGB)
	local selCol = vc1:AsColorStruct()
	vc1 = (vc1 + vc2) / 2
	local col = vc1:AsColorStruct()
	local blackCol = vc3:AsColorStruct()
	local cursorOffset1 = LM.Vector2:new_local()
	cursorOffset1:Set(0.08,0.02)
	moho.document:GetCameraMatrix(moho.frame, matrix)
	
	if (self.centerVec == nil) then
		self.centerVec = LM.Vector2:new_local()
		local center = LM.Vector2:new_local()
		center:Set(0,0)
		self.centerVec:Set(center)
	end
	centerVec:Set(self.centerVec)

	g:Push()
	g:ApplyMatrix(matrix)
	
	if moho.layer:ControllingSkeleton() ~= nil and moho.frame == 0 then
		if not self.skel or not self.skelLayer or not self.skelLayer:IsLayerValid(moho.layer) then
			self.skel = self:FindSkeleton(moho)
			self:FindSkeletonLayer(moho)
		end	

		local layer = moho.layer
		if layer:IsGroupType() or layer:LayerType() == MOHO.LT_PATCH then
			local ok = true
			if layer:LayerType() ~= MOHO.LT_GROUP and layer:LayerType() ~= MOHO.LT_PATCH  then
				if moho:LayerAsBone(layer) ~= nil then
					local skel = moho:LayerAsBone(layer):Skeleton()
					if skel:CountBones() > 0 then
						ok = false
					end
				end	
			end
			if ok then
				if self.skel:SelectedBoneID() >= 0 then
					for i=0, self.skel:CountBones() -1 do
						local bone = self.skel:Bone(i)
						if bone.fSelected then
							g:SetColor(selCol)
							g:SetSmoothing(true)
							if bone.fLength ~= 0 then 
								g:SetPenWidth(2)
								local boneStartPos = LM.Vector2:new_local()
								local boneLeftPos = LM.Vector2:new_local()
								local boneRightPos = LM.Vector2:new_local()
								local boneEndPos = LM.Vector2:new_local()
								boneLeftPos:Set(bone.fLength * 0.075, bone.fLength * 0.075)
								boneRightPos:Set(bone.fLength * 0.075, -bone.fLength * 0.075)
								boneEndPos:Set(bone.fLength, 0)
								local selBoneMatrix = LM.Matrix:new_local()
								selBoneMatrix:Set(bone.fRestMatrix)
								selBoneMatrix:Transform(boneStartPos)
								selBoneMatrix:Transform(boneEndPos)
								selBoneMatrix:Transform(boneLeftPos)
								selBoneMatrix:Transform(boneRightPos)
								local skelLayerMatrix = LM.Matrix:new_local()
								self.skelLayer:GetFullTransform(0, skelLayerMatrix, nil)
								skelLayerMatrix:Transform(boneStartPos)
								skelLayerMatrix:Transform(boneEndPos)
								skelLayerMatrix:Transform(boneLeftPos)
								skelLayerMatrix:Transform(boneRightPos)
								
								if self.isBones1353 then
									g:SetPenWidth(6)
									g:DrawLine(boneStartPos.x, boneStartPos.y, boneEndPos.x,  boneEndPos.y)
								else
									g:DrawLine(boneStartPos.x, boneStartPos.y, boneLeftPos.x,  boneLeftPos.y)
									g:DrawLine(boneLeftPos.x, boneLeftPos.y, boneEndPos.x,  boneEndPos.y)
									g:DrawLine(boneStartPos.x, boneStartPos.y, boneRightPos.x,  boneRightPos.y)
									g:DrawLine(boneRightPos.x, boneRightPos.y, boneEndPos.x,  boneEndPos.y)
								end
							else
								g:SetPenWidth(4)
								local boneStartPos = LM.Vector2:new_local()
								boneStartPos:Set(self:GetGlobalBonePos(moho, bone))
								local secondPos = LM.Vector2:new_local()
								secondPos:Set(1,0)
								local selBoneMatrix = LM.Matrix:new_local()
								selBoneMatrix:Set(bone.fRestMatrix)
								selBoneMatrix:Transform(secondPos)
								local skelLayerMatrix = LM.Matrix:new_local()
								self.skelLayer:GetFullTransform(0, skelLayerMatrix, nil)
								skelLayerMatrix:Transform(secondPos)
								local dist = self:GetDistance(boneStartPos, secondPos)
								if self.isBones1353 then
									g:FrameCircle(boneStartPos, (bone:DisplayWidth(1) * 0.0015) /g:CurrentScale())
								else
									g:FrameCircle(boneStartPos, 0.05 * dist)
								end	
							end	
						end	
					end
				end	
			end	
		end	
	end
	local lineSize = 0.03

	if self.showTransformInfo and self.transformInfo and HV_Font then
		g:SetPenWidth(1)
		g:SetColor(blackCol)
		g:SetSmoothing(true)
		HV_Font:DrawLetters(moho, g, self.transformInfoText, 2.5/g:CurrentScale(), self.mousePos.x + (cursorOffset1.x/g:CurrentScale()), self.mousePos.y + (cursorOffset1.y/g:CurrentScale()),2,2)
	end
	if self.centerId == 0 then
		g:SetPenWidth(2)
		g:SetColor(col)
		g:SetSmoothing(true)
		g:DrawLine(centerVec.x - lineSize, centerVec.y, centerVec.x + lineSize, centerVec.y)
		g:DrawLine(centerVec.x, centerVec.y - lineSize, centerVec.x, centerVec.y + lineSize)
	end
	g:Pop()
end

function MR_TransformRigTool:OnMouseDown(moho, mouseEvent)
	if not AE_Utilities then
		local ans = LM.GUI.Alert(LM.GUI.ALERT_WARNING, self:Localize('Missing Utilites'),
		self:Localize('Missing Utilites Info'), "", 'Ok', '', '', '')
	
		self.blockTransformation = true
		return
	end
	local skel = self:FindSkeleton(moho)
	if skel == nil or moho.frame ~= 0 then
		self.status = self:Localize('Could not find a Rig.')
		return
	end
	
	self.skel = skel
	self:FindSkeletonLayer(moho)
	self.blockTransformation = false
	local m = LM.Matrix:new_local()
	moho.layer:GetFullTransform(moho.frame, m, nil)
	if (mouseEvent.ctrlKey and mouseEvent.altKey) then
		local StartVec = LM.Vector2:new_local()
		StartVec:Set(mouseEvent.drawingStartVec)
		m:Transform(StartVec)
		self.centerVec:Set(StartVec)
		self.blockTransformation = true
		return
	end	
	
	if self.selectBonesBt then
		local id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, self.skelLayer, true)
		if (self.selectBonesBt) then
			if not mouseEvent.shiftKey and not mouseEvent.ctrlKey then
				skel:SelectNone()
			end
			if mouseEvent.ctrlKey then
				if id ~= -1 then
					skel:Bone(id).fSelected = false
				end
			else
				if id ~= -1 then
					skel:Bone(id).fSelected = true
				end
			end
			self.blockTransformation = true
			return
		end	
	else
		if mouseEvent.altKey and not mouseEvent.ctrlKey then
			local id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, self.skelLayer, true)
			skel:SelectNone()
			if id ~= -1 then
				skel:Bone(id).fSelected = true
			end
			self.blockTransformation = true
			return
		end
	end
	
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, skel)
	self:ScanLayers(moho)
	self.status = ' '
	
	if (self.vectorLayersToChange.layer[1] == nil) and self.imageLayersToChange.layer[1] == nil then
		if self.patchLayersToChange.layer[1] == nil then
			if (self.transformBones and self.mainSkelSelectedBones ~= 1) or not self.transformBones then
				self.status = self:Localize('No layers selected for active mode.')
				self:UpdateWidgets(moho)
				self.blockTransformation = true
				return
			end	
		end	
	end
	
	if (self.transformBones and self.mainSkelSelectedBones ~= 1) or (self.centerId == 1 and self.mainSkelSelectedBones ~= 1) then
		self.status = self:Localize('You need to select one bone in this mode.')
		self:UpdateWidgets(moho)
		self.blockTransformation = true
		return
	end
	
	self.isFlip = false
	self.lastScaleX = 1
	self.lastScaleY = 1
	if self.mainSkelSelectedBones == 1 then
		self.selectedMainBone = skel:Bone(skel:SelectedBoneID())
		self.selectedMainBone.fTempPos = self.selectedMainBone.fAnimPos:GetValue(0)
		self.selectedMainBone.fTempAngle = self.selectedMainBone.fAnimAngle:GetValue(0)
	end	

	if self.ignoreRefLayers then
		self:ScanGroupForReferences(moho, self.skelLayer)
	end	
	moho.document:PrepUndo(self.skelLayer)
	moho.document:SetDirty()
	
	if self.transformPoints then
		self:CollectPointsTempPos(moho)
	end	
	self.startAngle = 0
	local m = LM.Matrix:new_local()
	moho.document:GetCameraMatrix(moho.frame, m)
	self.lastVec:Set(mouseEvent.view:Point2Vec(mouseEvent.pt, m))
	self.offset:Set(0,0)
	
	if self.transformBones and self.mainSkelSelectedBones == 1 and self.scaleBt then
		local startBone = self.selectedMainBone
		startBone.fTempLength = startBone.fLength
		for l, b in ipairs(self.childBonesList) do
			self.childBonesList[l] = nil
		end
		
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if bone.fParent == skel:BoneID(startBone) then
				bone.fTempLength = bone.fLength
				bone.fTempPos = bone.fPos
				table.insert(self.childBonesList, bone)
				self:ScanBonesRecursion(moho, bone)
			end
		end
	end
	
	if self.transformImageLayers and (self.scaleBt or self.rotateBt) then
		local bone = skel:Bone(skel:SelectedBoneID())
		for i, id in ipairs(self.imageLayersToChange.layer) do
			local layer = moho.document:LayerByAbsoluteID(id)
			local vec = LM.Vector2:new_local()
			vec:Set(self.centerVec)
			local center = LM.Vector2:new_local()
			if self.centerId == 0 then -- custom center
				center:Set(self.centerVec)
			elseif self.centerId == 1 then -- selected bone
				center:Set(self:GetGlobalBonePos(moho, bone))
			end	
			center = self:GetLocalPos(moho, layer, center) -- use global pos
			self:SetOrigin(moho, layer, center)
		end
	end
	
	if self.centerId == 0 then -- custom center
		self.transformCenter:Set(self.centerVec)
	elseif self.centerId == 1 then -- selected bone
		local bone = skel:Bone(skel:SelectedBoneID())
		self.transformCenter:Set(self:GetGlobalBonePos(moho, bone, true))
	end	
	
	if self.rotateBt and self.followPathAdaptation then
		for i, id in ipairs(self.followPathGroupsToChange.group) do
			local group = moho.document:LayerByAbsoluteID(id)
			local vec = LM.Vector2:new_local()
			vec:Set(self.centerVec)
			local center = LM.Vector2:new_local()
			center:Set(self.transformCenter)
			center:Set(self:GetLocalPos(moho, group, center))
			self:SetOrigin(moho, group, center)
		end
	end
end

function MR_TransformRigTool:OnMouseMoved(moho, mouseEvent)
	if self.selectBonesBt then
		self.lastTransformMode = 0
	elseif self.translateBt then
		self:OnMouseMoved_T(moho, mouseEvent)
		self.transformType = 1
	elseif self.scaleBt then
		self:OnMouseMoved_S(moho, mouseEvent)
		self.transformType = 2
	elseif self.rotateBt then
		self:OnMouseMoved_R(moho, mouseEvent)
		self.transformType = 3
	end
end

function MR_TransformRigTool:OnMouseMoved_T(moho, mouseEvent)
	if self.blockTransformation then
		return
	end
	local skel = self.skel

	if skel == nil or moho.frame ~= 0 then
		return
	end
	
	local mouseStartVec = LM.Vector2:new_local()
	local mouseVec = LM.Vector2:new_local()
	
	local m = LM.Matrix:new_local()
	moho.document:GetCameraMatrix(moho.frame, m)
	mouseStartVec:Set(mouseEvent.view:Point2Vec(mouseEvent.startPt, m))
	mouseVec:Set(mouseEvent.view:Point2Vec(mouseEvent.pt, m))

	local offset = mouseVec - mouseStartVec
	if (mouseEvent.shiftKey) then
		if (math.abs(mouseVec.x - mouseStartVec.x) > math.abs(mouseVec.y - mouseStartVec.y)) then
			offset.y = 0
		else
			offset.x = 0
		end
	end
	self.offset:Set(offset)
	
	if self.transformPoints then
		for k, id in ipairs(self.vectorLayersToChange.layer) do
			local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
			local mesh = layer:Mesh()
			local localOffset = LM.Vector2:new_local()	
			local zeroOffset = LM.Vector2:new_local()	
			localOffset:Set(offset)
			localOffset:Set(self:GetLocalPos(moho, layer, localOffset))
			zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))

			localOffset:Set(localOffset - zeroOffset)
			mesh:TranslatePoints(localOffset)

			moho:AddPointKeyframe(0, layer) -- Fix Onion Skin
		end
	end
	
	if self.transformBones and self.mainSkelSelectedBones == 1 then 
		local localOffset = LM.Vector2:new_local()
		local zeroOffset = LM.Vector2:new_local()
		zeroOffset:Set(0, 0)
		localOffset:Set(self:GetLocalPos(moho, self.skelLayer, offset))
		zeroOffset:Set(self:GetLocalPos(moho, self.skelLayer, zeroOffset))
		localOffset:Set(localOffset - zeroOffset)
		local myBone = self.selectedMainBone
		if myBone.fParent >= 0 then
			local bonePos = LM.Vector2:new_local()		
			local newPos = LM.Vector2:new_local()
			bonePos:Set(myBone.fTempPos)
			local parentBone = skel:Bone(myBone.fAnimParent:GetValue(0))
			local parentMatrix = parentBone.fRestMatrix
			parentMatrix:Transform(bonePos)
			newPos:Set(bonePos + localOffset)
			local invertedMatrix = LM.Matrix:new_local()
			invertedMatrix:Set(parentMatrix)
			invertedMatrix:Invert()
			invertedMatrix:Transform(newPos)
			myBone.fAnimPos:SetValue(moho.frame, newPos)
		else
			myBone.fAnimPos:SetValue(0, myBone.fTempPos + localOffset)
		end
	end	
	self.showTransformInfo = true
	self.mousePos:Set(self:GetGlobalPos(moho, moho.layer, mouseEvent.drawingVec))
	
	self.transformInfoText = 'x '..self:Round(offset.x, 2)..' y '..self:Round(offset.y, 2)

	if self.ignoreRefLayers and self.liveUpdatingReferences then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end	
	
	if self.transformImageLayers then
		for i, id in ipairs(self.imageLayersToChange.layer) do
			local imagelayer = moho.document:LayerByAbsoluteID(id)
			local localOffset = LM.Vector2:new_local()
			local zeroOffset = LM.Vector2:new_local()
			localOffset:Set(offset)
			if imagelayer:Parent() then
				localOffset:Set(self:GetLocalPos(moho, imagelayer:Parent(), localOffset))
				zeroOffset:Set(self:GetLocalPos(moho, imagelayer:Parent(), zeroOffset))
				localOffset:Set(localOffset - zeroOffset)
			end
			local vec = LM.Vector3:new_local()
			vec:Set(self.imageLayersToChange.position[i])
			vec.x = vec.x + localOffset.x
			vec.y = vec.y + localOffset.y
			imagelayer.fTranslation:SetValue(0, vec)
		end	
	end
	moho:NewKeyframe(CHANNEL_LAYER_T)
	moho.document:DepthSort()
	
	self.lastTransformMode = 1
	self.lastTranslate:Set(offset)
	mouseEvent.view:DrawMe()
end

function MR_TransformRigTool:OnMouseMoved_S(moho, mouseEvent)
	if self.blockTransformation then
		return
	end
	
	local skel = self.skel

	if skel == nil or moho.frame ~= 0 then
		return
	end

	local bone = skel:Bone(skel:SelectedBoneID())
	local bonePos = LM.Vector2:new_local()

	local center = LM.Vector2:new_local()
	center:Set(self.transformCenter)
	local scaling = LM.Vector2:new_local()
	scaling:Set(1, 1)
	local v1 = LM.Vector2:new_local()
	local v2 = LM.Vector2:new_local()
	local m = LM.Matrix:new_local()
	moho.document:GetCameraMatrix(moho.frame, m)
	
	v1:Set(mouseEvent.view:Point2Vec(mouseEvent.startPt, m))
	v2:Set(mouseEvent.view:Point2Vec(mouseEvent.pt, m))
	v1 = v1 - center
	v2 = v2 - center
	scaling.x = v2.x / v1.x
	scaling.y = v2.y / v1.y
	
	self.lastTransformMode = 2
	self.lastCenter:Set(center)
	
	self.scaling = scaling
	if (mouseEvent.shiftKey and not self.transformBones) or (mouseEvent.shiftKey and self.centerId == 0)
		and not self.transformBones then
		scaling.y = 1 
		self.scalingMode = 2 -- h
		self.lastTransformMode = 3
	elseif (mouseEvent.ctrlKey and not self.transformBones) or (mouseEvent.ctrlKey and self.centerId == 0)
		and not self.transformBones then
		scaling.x = 1
		self.scalingMode = 3 -- v
		self.lastTransformMode = 4
	else
		scaling.x = (scaling.x + scaling.y) / 2
		scaling.y = scaling.x
		self.scalingMode = 1 -- uniform
		self.scaling = scaling
	end
	
	local flip = false
	if (scaling.x * self.lastScaleX < -0.0001) then
		if (scaling.y * self.lastScaleY > 0.0001) then
			flip = true
			self.isFlip = not self.isFlip
		end
	elseif (scaling.y * self.lastScaleY < -0.0001) then
		if (scaling.x * self.lastScaleX > 0.0001) then
			flip = true
			self.isFlip = not self.isFlip
		end
	end
	if (flip) then
		self.lastScaleX = scaling.x
		self.lastScaleY = scaling.y
	end
	
	if self.transformPoints then
		for k, id in ipairs(self.vectorLayersToChange.layer) do
			local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
			local mesh = layer:Mesh()
			local localCenter = LM.Vector2:new_local()
			localCenter:Set(center)
			localCenter = self:GetLocalPos(moho, layer, localCenter) -- use global pos
			mesh:ScalePoints(scaling.x, scaling.y, localCenter)
			moho:AddPointKeyframe(0, layer) -- Fix Onion Skin
			if flip then
				for i = 0, mesh:CountPoints() - 1 do
					local pt = mesh:Point(i)
					pt:FlipControlHandles(0)
				end	
			end	
		end
	end	
	
	if self.transformImageLayers then
		for i, id in ipairs(self.imageLayersToChange.layer) do
			local layer = moho.document:LayerByAbsoluteID(id)
			local vec = LM.Vector3:new_local()
			vec:Set(self.imageLayersToChange.scale[i])
			if self.scalingMode == 1 then
				vec.x = vec.x * scaling.x
				vec.y = vec.y * scaling.x
				vec.z = vec.z * scaling.x
			elseif self.scalingMode == 2 then
				vec.x = vec.x * scaling.x
			elseif self.scalingMode == 3 then
				vec.y = vec.y * scaling.y
			end
			layer.fScale:SetValue(0, vec)
		end	
	end
	moho:NewKeyframe(CHANNEL_LAYER_S)
	moho.document:DepthSort()
	
	if self.ignoreRefLayers and self.liveUpdatingReferences then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end	
	
	if self.transformBones and scaling.x == scaling.y and self.mainSkelSelectedBones == 1 then
		if self.centerId == 0 then
			local startBonePos = LM.Vector2:new_local()
			local centerVec = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			local dif = LM.Vector2:new_local()
			if bone.fParent < 0 then
				startBonePos:Set(bone.fTempPos)
				centerVec:Set(center)
				centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
				dif:Set(startBonePos - centerVec)
				newPos:Set((dif * scaling.x) + centerVec)
			else
				local parentBoneMatrix = LM.Matrix:new_local()
				local parentBone = skel:Bone(bone.fParent)
				startBonePos:Set(bone.fTempPos)
				parentBoneMatrix = parentBone.fRestMatrix
				parentBoneMatrix:Transform(startBonePos)
				centerVec:Set(center)
				centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
				dif:Set(startBonePos - centerVec)
				newPos:Set((dif * scaling.x) + centerVec)
				parentBoneMatrix:Invert()
				parentBoneMatrix:Transform(newPos)
			end
			bone.fAnimPos:SetValue(0, newPos)
		end
		self:ScaleBones(moho, 1, scaling)
	end
	
	self.showTransformInfo = true
	self.mousePos:Set(self:GetGlobalPos(moho, moho.layer, mouseEvent.drawingVec))
	
	if self.lastTransformMode == 2 then
		self.transformInfoText = LM.Round(scaling.x * 100)
	elseif self.lastTransformMode == 3 then
		self.transformInfoText = 'x '..LM.Round(scaling.x * 100)
	elseif self.lastTransformMode == 4 then
		self.transformInfoText = 'y '..LM.Round(scaling.y * 100)	
	end	
	
	self.lastScaling:Set(scaling)
	mouseEvent.view:DrawMe()
end

function MR_TransformRigTool:OnMouseMoved_R(moho, mouseEvent)
	if self.blockTransformation then
		return
	end
	
	local skel = self.skel

	if skel == nil or moho.frame ~= 0 then
		return
	end
	
	local bone = skel:Bone(skel:SelectedBoneID())
	local bonePos = LM.Vector2:new_local()

	local center = LM.Vector2:new_local()
	center:Set(self.transformCenter)
	self.lastCenter:Set(center)
	local m = LM.Matrix:new_local()
	moho.document:GetCameraMatrix(moho.frame, m)
	local mousePos = mouseEvent.view:Point2Vec(mouseEvent.pt, m)
	local angle = self.startAngle
	local v1 = self.lastVec - center
	local v2 = mousePos - center
	v2:Rotate(-math.atan2(v1.y, v1.x))
	angle = angle + math.atan2(v2.y, v2.x)
	self.startAngle = angle
	if (mouseEvent.shiftKey) then
		angle = angle / math.rad(45)
		angle = math.rad(45) * LM.Round(angle)
	end
	
	if self.transformPoints then
		for k, id in ipairs(self.vectorLayersToChange.layer) do
			local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
			local mesh = layer:Mesh()
			local localCenter = LM.Vector2:new_local()
			localCenter:Set(center)
			localCenter = self:GetLocalPos(moho, layer, localCenter) -- use global 
			local localAngle = angle
			if self.vectorLayersToChange.parentalFlip[k] then
				localAngle = -localAngle
			end
			mesh:RotatePoints(localAngle, localCenter)
			moho:AddPointKeyframe(0, layer) -- Fix Onion Skin
		end	
	end
	
	self.lastVec:Set(mousePos)
	self.lastAngle = angle
	
	if self.transformImageLayers then
		for i, id in ipairs(self.imageLayersToChange.layer) do
			local imageLayer = moho.document:LayerByAbsoluteID(id)
			local isLayerinFollowPathGroup = false
			if self.followPathAdaptation then
				for i, groupId in ipairs(self.followPathGroupsToChange.group) do
					if imageLayer:Parent() then
						local parentLayerID = moho.document:LayerAbsoluteID(imageLayer:Parent())
						if parentLayerID == groupId then
							isLayerinFollowPathGroup = true
						end
					end
				end
			end
			if not isLayerinFollowPathGroup then
				local imageAngle = self.imageLayersToChange.angle[i]
				local localAngle = self.lastAngle
				if self.imageLayersToChange.parentalFlip[i] then
					localAngle = -localAngle
				end
				imageAngle = imageAngle + localAngle
				imageLayer.fRotationZ:SetValue(0, imageAngle)
			end	
		end	
	end
	
	if self.followPathAdaptation then
		for i, id in ipairs(self.followPathGroupsToChange.group) do
			local group = moho.document:LayerByAbsoluteID(id)
			if self.followPathGroupsToChange.transform[i] then
				local groupAngle = self.followPathGroupsToChange.angle[i]
				local localAngle = self.lastAngle
				if self.followPathGroupsToChange.parentalFlip[i] then
					localAngle = -localAngle
				end
				groupAngle = groupAngle + localAngle
				group.fRotationZ:SetValue(0, groupAngle)
			end	
		end	
	end
	
	moho:NewKeyframe(CHANNEL_LAYER_R)
	moho.document:DepthSort()
	
	if self.transformBones and self.mainSkelSelectedBones == 1 then
		local bone = self.selectedMainBone
		local startBonePos = LM.Vector2:new_local()
		local centerVec = LM.Vector2:new_local()
		local newPos = LM.Vector2:new_local()
		local dif = LM.Vector2:new_local()
		startBonePos:Set(bone.fTempPos)
		centerVec:Set(center)
		centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
		if bone.fParent < 0 then
			dif:Set(startBonePos - centerVec)
			local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
			local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
			newPos:Set(px + centerVec.x, py + centerVec.y)
		else	
			local parentBoneMatrix = LM.Matrix:new_local()
			local parentBone = skel:Bone(bone.fParent)
			parentBoneMatrix = parentBone.fRestMatrix
			parentBoneMatrix:Transform(startBonePos)
			dif:Set(startBonePos - centerVec)
			local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
			local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
			newPos:Set(px + centerVec.x, py + centerVec.y)
			parentBoneMatrix:Invert()
			parentBoneMatrix:Transform(newPos)
		end
		bone.fAnimPos:SetValue(0, newPos)
		bone.fAnimAngle:SetValue(0, bone.fTempAngle + self.lastAngle)
	end
	
	self.showTransformInfo = true
	self.mousePos:Set(self:GetGlobalPos(moho, self.skelLayer, self.lastVec))
	self.transformInfoText = LM.Round(180 * self.lastAngle/math.pi)
	
	if self.ignoreRefLayers and self.liveUpdatingReferences then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end	
	
	mouseEvent.view:DrawMe()
	self.lastTransformMode = 5
end

function MR_TransformRigTool:OnMouseUp(moho, mouseEvent)
	if self.blockTransformation then
		self.blockTransformation = false
		return
	end
	local skel = self.skel
	self.showTransformInfo = false
	local curLayer = moho.layer
	if skel == nil or moho.frame ~= 0 then
		return
	end
	
	for m in pairs(self.fixedActionsList) do
		self.fixedActionsList[m] = nil
	end
	
	self.lastScaling:Set(self.scaling)
	self.lastCenterId = self.centerId
	local isMarker = self:CheckMarker(moho)
	
	if moho.frame == 0 and self.transformType == 1 then -- translate
		if self.transformPoints then
			self:TranslatePointsInActions(moho, self.offset)
		end	
		if self.transformBones and self.mainSkelSelectedBones == 1 then
			if self.transformTargetBones and not isMarker then
				self:TransformTargetBones(moho, false)
			end
			if self.transformVitruvianBones and self.isVitruvianBonesAvaible and not isMarker then
				self:TranslateVitruvianBones(moho, false)
			end
			if not isMarker then
				local bone = skel:Bone(skel:SelectedBoneID())
				self:TranslateBoneInActions(moho, bone, self.offset)
			end	
		end	
		if self.transformOrigin then
			self:TranslateOrigins(moho, self.offset)
		end
		
		if self.followPathAdaptation then
			for k, id in ipairs(self.vectorLayersToChange.layer) do
				local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
				if self.vectorLayersToChange.transformOriginsOffset[k] then
					local newOriginPos = LM.Vector2:new_local()
					local origin = LM.Vector2:new_local()
					origin:Set(self.vectorLayersToChange.origin[k])
					local newOffset = LM.Vector2:new_local()
					newOffset:Set(self.offset)
					local zeroOffset = LM.Vector2:new_local()
					newOffset:Set(self:GetLocalPos(moho, layer, newOffset))
					zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
					newOffset:Set(newOffset - zeroOffset)
					newOriginPos:Set(origin + newOffset)
					self:SetOrigin(moho, layer, newOriginPos)
					layer.fTranslation:SetValue(0, self.vectorLayersToChange.layersPosition[k])
				end	
			end
			for k, id in ipairs(self.followPathGroupsToChange.group) do
				local layer = moho.document:LayerByAbsoluteID(id)
				if self.followPathGroupsToChange.transform[k] then
					local newOriginPos = LM.Vector2:new_local()
					local origin = self.followPathGroupsToChange.origin[k]
					local newOffset = LM.Vector2:new_local()
					newOffset:Set(self.offset)
					
					local zeroOffset = LM.Vector2:new_local()
					newOffset:Set(self:GetLocalPos(moho, layer, newOffset))
					zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
					newOffset:Set(newOffset - zeroOffset)
					newOriginPos:Set(origin + newOffset)
					self:SetOrigin(moho, layer, newOriginPos)
					layer.fTranslation:SetValue(0, self.followPathGroupsToChange.layersPosition[k])
				end	
			end
		end
		
		if self.transformImageLayers then
			self:TranslateImagesInActions(moho, self.offset, 0)
		end
		
		if self.transformPatchLayers then
			for i, id in ipairs(self.patchLayersToChange.layer) do
				local layer = moho.document:LayerByAbsoluteID(id)
				self:TranslateLayer(moho, layer, self.offset)
			end	
		end
	end
	
	if moho.frame == 0 and self.transformType == 2 then -- scale
		local bone = skel:Bone(skel:SelectedBoneID())
		local ok = true
		if self.centerId == 1 and skel:SelectedBoneID() == -1 then
			ok = false
		elseif self.scaling.x == 1 and 	self.scaling.y == 1 then
			ok = false
		end
		
		if self.transformBones and self.mainSkelSelectedBones ~= 1 then
			ok = false
		end
		
		if ok then
			if self.transformPoints then
				if self.adjustStrokeWidth and self.lastTransformMode == 2 then
					self:AdjustStrokesWidth(moho, self.scaling.x)
				end	
				self:ScalePointsInActions(moho, self.lastCenter, self.scaling)
			end
			if self.transformBones and self.mainSkelSelectedBones == 1 then
				if self.transformBones and self.scaling.x == self.scaling.y then
					if not isMarker then
						if self.adaptiveScaleAndRotation then
							self:CorrectScalingOffsetInActions(moho, bone, self.lastCenter, self.scaling)
						else	
							local startBonePos = LM.Vector2:new_local()
							local centerVec = LM.Vector2:new_local()
							local newPos = LM.Vector2:new_local()
							local dif = LM.Vector2:new_local()
							local offset = LM.Vector2:new_local()
								startBonePos:Set(bone.fTempPos)
							if bone.fParent < 0 then
								centerVec:Set(self.lastCenter)
								centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
								dif:Set(startBonePos - centerVec)
								newPos:Set((dif * self.scaling.x) + centerVec)
							else
								local parentBoneMatrix = LM.Matrix:new_local()
								local parentBone = skel:Bone(bone.fParent)
								parentBoneMatrix = parentBone.fRestMatrix
								parentBoneMatrix:Transform(startBonePos)
								centerVec:Set(self.lastCenter)
								centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
								dif:Set(startBonePos - centerVec)
								newPos:Set((dif * self.scaling.x) + centerVec)
							end
							offset:Set(self:GetGlobalPos(moho, self.skelLayer, newPos) - self:GetGlobalPos(moho, self.skelLayer, startBonePos))
							self:TranslateBoneInActions(moho, bone, offset)
						end
						self:ScaleBonesInActions(moho, self.lastCenter, self.scaling)
					end	
				end
				if self.transformVitruvianBones and self.isVitruvianBonesAvaible and not isMarker then
					self:ScaleVitruvianBones(moho, false)
				end
				if self.transformTargetBones and not isMarker then
					self:TransformTargetBones(moho, false)
				end
			end
			if self.transformOrigin then
				self:ScaleOrigins(moho, self.lastCenter, self.scaling)
			end
			
			if self.followPathAdaptation then
				for k, id in ipairs(self.vectorLayersToChange.layer) do
					local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
					if self.vectorLayersToChange.transformOriginsOffset[k] then
						local centerVec = LM.Vector2:new_local()
						centerVec:Set(self.lastCenter)
						centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
						local dif = LM.Vector2:new_local()
						local newOriginPos = LM.Vector2:new_local()
						local origin = self.vectorLayersToChange.origin[k]
						dif:Set(origin - centerVec)
						newOriginPos:Set((dif * self.scaling.x) + centerVec)
						self:SetOrigin(moho, layer, newOriginPos)
						layer.fTranslation:SetValue(0, self.vectorLayersToChange.layersPosition[k])
					end	
				end
				for k, id in ipairs(self.followPathGroupsToChange.group) do
					local layer = moho.document:LayerByAbsoluteID(id)
					if self.followPathGroupsToChange.transform[k] then
						local centerVec = LM.Vector2:new_local()
						centerVec:Set(self.lastCenter)
						centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
						local dif = LM.Vector2:new_local()
						local newOriginPos = LM.Vector2:new_local()
						local origin = self.followPathGroupsToChange.origin[k]
						dif:Set(origin - centerVec)
						newOriginPos:Set((dif * self.scaling.x) + centerVec)
						self:SetOrigin(moho, layer, newOriginPos)
						layer.fTranslation:SetValue(0, self.followPathGroupsToChange.layersPosition[k])
					end	
				end
			end	
			if self.transformImageLayers then
				for i, id in ipairs(self.imageLayersToChange.layer) do
					local imagelayer = moho.document:LayerByAbsoluteID(id)
					self:SetOrigin(moho, imagelayer, self.imageLayersToChange.origin[i])
					local posDif = LM.Vector3:new_local()
					posDif:Set(imagelayer.fTranslation:GetValue(0) - self.imageLayersToChange.position[i])
					for actionID = 0, imagelayer.fTranslation:CountActions() - 1 do
						local action = moho:ChannelAsAnimVec3(imagelayer.fTranslation:Action(actionID))
						local actionName = imagelayer.fTranslation:ActionName(actionID)
						if action ~= nil then
							for keyID = 0, action:CountKeys() - 1 do
								local actionFrame = action:GetKeyWhen(keyID)
								if (actionFrame > 0) then
									local newPos = LM.Vector3:new_local()
									local layerTranslation = action:GetValue(actionFrame)
									newPos:Set(layerTranslation + posDif)
									action:SetValue(actionFrame, newPos)
									if layerTranslation ~= newPos then
										self:CollectLog(nil, actionName)
									end	
								end
							end
						end	
					end
				end	
				self:ScaleImagesInActions(moho, self.lastCenter, self.scaling)
			end
			if self.transformPatchLayers then
				for i, id in ipairs(self.patchLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:ScaleLayer(moho, layer, self.lastCenter, self.scaling)
				end	
			end
		else
			self.status = self:Localize('Only one bone should be selected.')
		end	
	end
	
	if moho.frame == 0 and self.transformType == 3 then -- rotate
		local bone = skel:Bone(skel:SelectedBoneID())
		local ok = true
		if self.centerId == 1 and skel:SelectedBoneID() == -1 then
			ok = false
		end
		if ok then
			local bonePos = LM.Vector2:new_local()
			local center = LM.Vector2:new_local()
			if self.centerId == 0 then -- custom center
				center:Set(self.centerVec)
			elseif self.centerId == 1 then -- selected bone
				bonePos:Set(self:GetGlobalBonePos(moho, bone))
				center:Set(bonePos)
			end
			if self.transformPoints then
				self:RotatePointsInActions(moho, center, self.lastAngle)
			end	
			if self.transformOrigin then
				self:RotateOrigins(moho, self.lastCenter, self.lastAngle)
			end
			
			if self.transformBones and self.mainSkelSelectedBones == 1 then
				if self.transformVitruvianBones and self.isVitruvianBonesAvaible and not isMarker then
					self:RotateVitruvianBones(moho, false)
					end	
				if self.transformTargetBones and not isMarker then
					self:TransformTargetBones(moho, false)
				end
				if not isMarker then
					if self.adaptiveScaleAndRotation then
						self:CorrectRotatingOffsetInActions(moho, bone, center, self.lastAngle)
					else	
						local startBonePos = LM.Vector2:new_local()
						local centerVec = LM.Vector2:new_local()
						local newPos = LM.Vector2:new_local()
						local dif = LM.Vector2:new_local()
						local offset = LM.Vector2:new_local()
						local angle = self.lastAngle
						startBonePos:Set(bone.fTempPos)
						centerVec:Set(center)
						centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
						if bone.fParent < 0 then
							dif:Set(startBonePos - centerVec)
							local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
							local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
							newPos:Set(px + centerVec.x, py + centerVec.y)
						else
							local parentBoneMatrix = LM.Matrix:new_local()
							local parentBone = skel:Bone(bone.fParent)
							parentBoneMatrix = parentBone.fRestMatrix
							parentBoneMatrix:Transform(startBonePos)
							dif:Set(startBonePos - centerVec)
							local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
							local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
							newPos:Set(px + centerVec.x, py + centerVec.y)
						end
						offset:Set(self:GetGlobalPos(moho, self.skelLayer, newPos) - self:GetGlobalPos(moho, self.skelLayer, startBonePos))
						self:TranslateBoneInActions(moho, bone, offset)
					end
					self:RotateBoneInActions(moho, bone, self.lastAngle)
				end	
			end	
			if self.transformImageLayers then
				for i, id in ipairs(self.imageLayersToChange.layer) do
					local imagelayer = moho.document:LayerByAbsoluteID(id)
					self:SetOrigin(moho, imagelayer, self.imageLayersToChange.origin[i])
					local posDif = LM.Vector3:new_local()
					posDif:Set(imagelayer.fTranslation:GetValue(0) - self.imageLayersToChange.position[i])
					for actionID = 0, imagelayer.fTranslation:CountActions() - 1 do
						local action = moho:ChannelAsAnimVec3(imagelayer.fTranslation:Action(actionID))
						local actionName = imagelayer.fTranslation:ActionName(actionID)
						if action ~= nil then
							for keyID = 0, action:CountKeys() - 1 do
								local actionFrame = action:GetKeyWhen(keyID)
								if (actionFrame > 0) then
									local newPos = LM.Vector3:new_local()
									local layerTranslation = action:GetValue(actionFrame)
									newPos:Set(layerTranslation + posDif)
									
									action:SetValue(actionFrame, newPos)
									if layerTranslation ~= newPos then
										self:CollectLog(nil, actionName)
									end	
								end
							end
						end	
					end
				end	
				self:RotateImagesInActions(moho, self.lastCenter, self.lastAngle)
			end
			if self.followPathAdaptation then
				for i, id in ipairs(self.followPathGroupsToChange.group) do
					local group = moho.document:LayerByAbsoluteID(id)
					self:SetOrigin(moho, group, self.followPathGroupsToChange.origin[i])
				end	
			end		
			if self.transformPatchLayers then
				for i, id in ipairs(self.patchLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:RotateLayer(moho, layer, center, self.lastAngle)
				end	
			end
		end
	end
	
	if self.transformPoints then
		self:RefreshCachedLayers(moho)
	end
	
	if self.ignoreRefLayers and not self.liveUpdatingReferences then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end
	
	local totalActions = #self.fixedActionsList
	local actionsStr = self:Localize(' actions were updated.')
	if totalActions == 1 then
		actionsStr = self:Localize(' action was updated.')
	end
	if totalActions  > 0 then
		self.status = totalActions.. actionsStr
	else
		self.status = self:Localize('Actions did not need to be updated')
	end
	
	self.scaling:Set(1,1)
	self.transformType = 0
	self.isFlip = false	
	moho:NewKeyframe(CHANNEL_POINT)
	moho:UpdateUI()
	moho.view:DrawMe()
end

function MR_TransformRigTool:ScanLayers(moho, alt)
	if self.adjustStrokeWidth then
		for k in pairs(self.vectorLayersToStrokeChange) do
			self.vectorLayersToStrokeChange[k] = nil
		end
	end	
	
	for k in pairs(self.vectorLayersToChange.layer) do
		self.vectorLayersToChange.layer[k] = nil
		self.vectorLayersToChange.parentalFlip[k] = nil
		self.vectorLayersToChange.layersPosition[k] = nil
		self.vectorLayersToChange.origin[k] = nil
		self.vectorLayersToChange.transformOriginsOffset[k] = nil
	end
	
	for k in pairs(self.groupsToChange.group) do
		self.groupsToChange.group[k] = nil
	end
	
	for k in pairs(self.followPathGroupsToChange.group) do
		self.followPathGroupsToChange.group[k] = nil
		self.followPathGroupsToChange.origin[k] = nil
		self.followPathGroupsToChange.angle[k] = nil
		self.followPathGroupsToChange.parentalFlip[k] = nil
		self.followPathGroupsToChange.transform[k] = nil
		self.followPathGroupsToChange.layersPosition[k] = nil
	end
	
	for k in pairs(self.patchLayersToChange.layer) do
		self.patchLayersToChange.layer[k] = nil
	end
	
	for k in pairs(self.imageLayersToChange.layer) do
		self.imageLayersToChange.layer[k] = nil
		self.imageLayersToChange.origin[k] = nil
		self.imageLayersToChange.scale[k] = nil
		self.imageLayersToChange.position[k] = nil
		self.imageLayersToChange.angle[k] = nil
		self.imageLayersToChange.parentalFlip[k] = nil
	end
	
	local count = 0
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		if layer then
			count = count + 1
			if layer:IsAncestorSelected() or layer:SecondarySelection() or layer == moho.layer then
				local isLayerInSkel = false
				if layer:ControllingSkeleton() then
					if layer:ControllingSkeleton() == self.skel then
						if layer:LayerType() == MOHO.LT_BONE then
							local layerSkel = moho:LayerAsBone(layer):Skeleton()
							if layerSkel:CountBones() < 1 then
								isLayerInSkel = true
							end	
						else
							isLayerInSkel = true
						end	
					end
				else
					if layer:LayerType() == MOHO.LT_BONE then
						local layerSkel = moho:LayerAsBone(layer):Skeleton()
						if layerSkel then
							if layerSkel == self.skel then
								isLayerInSkel = true
							end
						end	
					else
						local parentGroup = layer:Parent()
						if parentGroup ~= nil then
							local targetGroup = parentGroup
							repeat
								if targetGroup:LayerType() == MOHO.LT_BONE then
									local layerSkel = moho:LayerAsBone(targetGroup):Skeleton()
									if targetGroup == self.skel then
										isLayerInSkel = true
										break
									else
										
										break
									end
								else
									if targetGroup:ControllingSkeleton() then
										local targetSkel = targetGroup:ControllingSkeleton()
										if targetSkel ~= nil then
											if targetSkel == self.skel then
												isLayerInSkel = true
												break
											else	
												break
											end	
										end
									end
								end	
								targetGroup = targetGroup:Parent()
							until targetGroup == nil 
						end
					end
				end
				if isLayerInSkel then
					if self.transformImageLayers then
						if moho:LayerAsImage(layer) then
							if not self.ignoreRefLayers or (self.ignoreRefLayers and not layer:IsReferencedLayer()) then
								table.insert(self.imageLayersToChange.layer, moho.document:LayerAbsoluteID(layer))
								local scale = LM.Vector3:new_local()
								local translation = LM.Vector3:new_local()
								scale:Set(layer.fScale.value)
								translation:Set(layer.fTranslation.value)
								local angle = layer.fRotationZ.value
								table.insert(self.imageLayersToChange.origin, layer:Origin())
								table.insert(self.imageLayersToChange.scale, scale)
								table.insert(self.imageLayersToChange.position, translation)
								table.insert(self.imageLayersToChange.angle, angle)
								if self.rotateBt then
									table.insert(self.imageLayersToChange.parentalFlip, self:CheckLayerParentalFlip(moho, layer))
								end
							end
						end
					end	
					if moho:LayerAsVector(layer) and self.transformPoints then
						if not self.ignoreRefLayers or (self.ignoreRefLayers and not layer:IsReferencedLayer()) then
							local isLayerOk = true
							local transformOffset = false
							if (self.followPathAdaptation and self.rotateBt) or (self.followPathAdaptation and alt) then
								if layer:GetFollowingLayer() then
									if layer:Parent() then
										local layerParent = layer:Parent()
										if layerParent ~= self.skelLayer then
											local isGroupNew = true
											local layerParentID = moho.document:LayerAbsoluteID(layerParent)
											for _, groupID in pairs(self.followPathGroupsToChange.group) do
												if groupID == layerParentID then
													isGroupNew = false
												end
											end
											if isGroupNew then
												table.insert(self.followPathGroupsToChange.group, layerParentID)
												table.insert(self.followPathGroupsToChange.origin, layerParent:Origin())
												table.insert(self.followPathGroupsToChange.transform, true)
												table.insert(self.followPathGroupsToChange.layersPosition, layerParent.fTranslation:GetValue(0))
												table.insert(self.followPathGroupsToChange.angle, layerParent.fRotationZ:GetValue(0))
												table.insert(self.followPathGroupsToChange.parentalFlip, self:CheckLayerParentalFlip(moho, layerParent))
											end
										else
											local isLayerOk = false
										end
									else
										local isLayerOk = false
									end
								end
							elseif self.followPathAdaptation and (self.translateBt or self.scaleBt) then
								if layer:GetFollowingLayer() then
									local mesh = moho:LayerAsVector(layer):Mesh()
									if mesh:CountPoints() > 1 then
										local point = mesh:Point(0)
										local followLayer = layer:GetFollowingLayer()
										local followCurve = layer:GetFollowingCurve()
										local followValue = layer.fFollowing.value
										local matrix = LM.Matrix:new_local()
										layer:GetFullTransform(0, matrix, nil)
										local curPos = point.fAnimPos:GetValue(0)
										local newPos = LM.Vector2:new_local()
										newPos:Set(curPos)
										matrix:Transform(curPos)
										layer:SetFollowingCurve(followLayer, followCurve, followValue, true)
										layer:GetFullTransform(0, matrix, nil)
										matrix:Transform(newPos)
										if self:Round(curPos.x, 5) == self:Round(newPos.x, 5) and self:Round(curPos.y, 5) == self:Round(newPos.y, 5) then
										else
											layer:SetFollowingCurve(followLayer, followCurve, followValue, false)
											transformOffset = true
										end
									end	
								end
							end
							if self.adjustStrokeWidth then
								table.insert(self.vectorLayersToStrokeChange, moho.document:LayerAbsoluteID(layer))
							end	
							if isLayerOk then
								local mesh = moho:LayerAsVector(layer):Mesh()
								if self.autoSelect then
									mesh:SelectAll()
								end	
								if (mesh:CountPoints() > 1 and mesh ~= nil) then
									table.insert(self.vectorLayersToChange.layer, moho.document:LayerAbsoluteID(layer))
									if self.followPathAdaptation then
										table.insert(self.vectorLayersToChange.layersPosition, layer.fTranslation:GetValue(0))
										table.insert(self.vectorLayersToChange.transformOriginsOffset, transformOffset)
									end	
									table.insert(self.vectorLayersToChange.origin, layer:Origin())
									if self.rotateBt then
										table.insert(self.vectorLayersToChange.parentalFlip, self:CheckLayerParentalFlip(moho, layer, true))
									end
								end
							end	
						end
					end
					if layer:IsGroupType() then
						if layer:GetFollowingLayer() and self.followPathAdaptation then
							local isGroupNew = true
							local layerID = moho.document:LayerAbsoluteID(layer)
							for _, groupID in pairs(self.followPathGroupsToChange.group) do
								if groupID == layerID then
									isGroupNew = false
								end
							end
							if isGroupNew then
								table.insert(self.followPathGroupsToChange.group, layerID)
								table.insert(self.followPathGroupsToChange.origin, layer:Origin())
								if self.translateBt or self.scaleBt then
									table.insert(self.followPathGroupsToChange.transform, true)
								else
									table.insert(self.followPathGroupsToChange.transform, false)
								end	
								table.insert(self.followPathGroupsToChange.layersPosition, layer.fTranslation:GetValue(0))
								table.insert(self.followPathGroupsToChange.angle, layer.fRotationZ:GetValue(0))
								if self.rotateBt then
									table.insert(self.followPathGroupsToChange.parentalFlip, self:CheckLayerParentalFlip(moho, layer))
								end
							end
						else	
							table.insert(self.groupsToChange.group, moho.document:LayerAbsoluteID(layer))
						end	
					end
					if layer:LayerType() == MOHO.LT_PATCH and self.transformPatchLayers then
						table.insert(self.patchLayersToChange.layer, moho.document:LayerAbsoluteID(layer))
					end
				end	
			end
		end
	until not layer
	if self.followPathAdaptation then
		if self.rotateBt or alt then
			local isChanges = false
			repeat
				::start_loop_groups::
				isChanges = false
				for k, id in pairs(self.followPathGroupsToChange.group) do
					local layer = moho.document:LayerByAbsoluteID(id)
					for i, targetId in pairs(self.followPathGroupsToChange.group) do
						local targetLayer = moho:LayerAsGroup(moho.document:LayerByAbsoluteID(targetId))
						if targetLayer:IsLayerValid(layer) then
							table.remove(self.followPathGroupsToChange.group, k)
							table.remove(self.followPathGroupsToChange.origin, k)
							table.remove(self.followPathGroupsToChange.angle, k)
							table.remove(self.followPathGroupsToChange.layersPosition, k)
							if self.rotateBt then
								table.remove(self.followPathGroupsToChange.parentalFlip, k)
							end	
							table.remove(self.followPathGroupsToChange.transform, k)
							isChanges = true
							goto start_loop_groups
						end
					end
				end
			until not isChange
			repeat
				::start_loop_vector_layers::
				isChanges = false
				for k, id in pairs(self.vectorLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					for i, targetId in pairs(self.followPathGroupsToChange.group) do
						local targetLayer = moho:LayerAsGroup(moho.document:LayerByAbsoluteID(targetId))
						if targetLayer:IsLayerValid(layer) then
							table.remove(self.vectorLayersToChange.layer, k)
							table.remove(self.vectorLayersToChange.layersPosition, k)
							table.remove(self.vectorLayersToChange.origin, k)
							table.remove(self.vectorLayersToChange.transformOriginsOffset, k)
							if self.rotateBt then
								table.remove(self.vectorLayersToChange.parentalFlip, k)
							end	
							isChanges = true
							goto start_loop_vector_layers
						end
					end
				end
			until not isChange
		end	
	end
end

function MR_TransformRigTool:FlipPoints(moho, direction, center, alt)
	local skel = self.skel

	if skel == nil or moho.frame ~= 0 then
		return
	end
	
	if self.mainSkelSelectedBones ~= 1 and self.centerId == 1 then
		return
	end
	
	if (self.vectorLayersToChange.layer[1] == nil) then
		return
	end
	self:FindSkeletonLayer(moho)
	if self.ignoreRefLayers then
		self:ScanGroupForReferences(moho, self.skelLayer)
	end
	
	for i, l in ipairs(self.curvesList) do
		self.curvesList[i] = nil
	end
	for i, l in ipairs(self.fPoses) do
		self.fPoses[i] = nil
	end
	for i, l in ipairs(self.selList) do
		self.selList[i] = nil
	end
	
	local bone = skel:Bone(skel:SelectedBoneID())
	local bonePos = LM.Vector2:new_local()	
	
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		
		if self.followPathAdaptation then
			for i, groupId in ipairs(self.followPathGroupsToChange.group) do
				if layer:Parent() then
					local parentLayerID = moho.document:LayerAbsoluteID(layer:Parent())
					if parentLayerID == groupId then
						goto continue_to_next_layer
					end
				end
			end
		end	
		
		local centerVec = LM.Vector2:new_local()
		centerVec:Set(center)
		local mesh = layer:Mesh()
		centerVec = self:GetLocalPos(moho, layer, centerVec) -- use global pos
		if self.autoSelect then
			mesh:SelectAll()
		end
		local localDirection = direction
		
		local skelMatrix = LM.Matrix:new_local()
		if alt and not self.useGlobalFlip then
			if self.lastSkelLayer ~= nil then
				self.lastSkelLayer:GetFullTransform(0, skelMatrix, nil)
			end
		elseif self.useGlobalFlip or not alt then
			self.skelLayer:GetFullTransform(0, skelMatrix, nil)
		end
		local layerMatrix = LM.Matrix:new_local()
		layer:GetFullTransform(0, layerMatrix, nil)
		
		local invertedLayerMatrix = LM.Matrix:new_local()
		invertedLayerMatrix:Set(layerMatrix)
		invertedLayerMatrix:Invert()
		
		local invertedSkelMatrix = LM.Matrix:new_local()
		invertedSkelMatrix:Set(skelMatrix)
		invertedSkelMatrix:Invert()
		
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				local dif = LM.Vector2:new_local()
				local newPos = LM.Vector2:new_local()
				local pointPos = LM.Vector2:new_local()
				local localCener = LM.Vector2:new_local()
				localCener:Set(center)
				pointPos:Set(pt.fAnimPos:GetValue(0))
				layerMatrix:Transform(pointPos)
				if not self.useGlobalFlip then	
					invertedSkelMatrix:Transform(localCener)
					invertedSkelMatrix:Transform(pointPos)
				end
				if direction then
					dif:Set(localCener.x - pointPos.x, 0)
					newPos:Set(localCener.x + dif.x, pointPos.y)
				else
					dif:Set(0, localCener.y - pointPos.y)
					newPos:Set(pointPos.x, localCener.y + dif.y)
				end
				if not self.useGlobalFlip then
					skelMatrix:Transform(newPos)
				end	
				invertedLayerMatrix:Transform(newPos)
				pt.fAnimPos:SetValue(0, newPos)
				
				pt:FlipControlHandles(0)
				for actionID = 0, pt.fAnimPos:CountActions() - 1 do
					local action = moho:ChannelAsAnimVec2(pt.fAnimPos:Action(actionID))
					local actionName = pt.fAnimPos:ActionName(actionID)
					if action ~= nil then
						layer:ActivateAction(actionName)
						for keyID = 0, action:CountKeys() - 1 do
							local posFrame = action:GetKeyWhen(keyID)
							if (posFrame > 0) then
								if alt or self.useGlobalFlip then
									local pointPos = LM.Vector2:new_local()
									local originalPos = LM.Vector2:new_local()
									local localCener = LM.Vector2:new_local()
									localCener:Set(center)
									pointPos:Set(action:GetValue(posFrame))
									originalPos:Set(pointPos)
									layerMatrix:Transform(pointPos)
									if alt and not self.useGlobalFlip then	
										invertedSkelMatrix:Transform(localCener)
										invertedSkelMatrix:Transform(pointPos)
									end
									if localDirection then
										dif:Set(localCener.x - pointPos.x, 0)
										newPos:Set(localCener.x + dif.x, pointPos.y)
									else
										dif:Set(0, localCener.y - pointPos.y)
										newPos:Set(pointPos.x, localCener.y + dif.y)
									end
									
									if alt and not self.useGlobalFlip then
										skelMatrix:Transform(newPos)
									end	
									invertedLayerMatrix:Transform(newPos)
								else
									if localDirection then
										dif:Set(centerVec.x - action:GetValue(posFrame).x, 0)
										newPos:Set(centerVec.x + dif.x, action:GetValue(posFrame).y)
									else
										dif:Set(0, centerVec.y - action:GetValue(posFrame).y)
										newPos:Set(pt.fAnimPos:GetValue(posFrame).x, centerVec.y + dif.y)
									end
								end	
								action:SetValue(posFrame, newPos)
								if originalPos ~= newPos then
									self:CollectLog(nil, actionName)
								end	
							end
						end
					end	
				end
			end
		end	
				
		self:FlipControlHandlesInActions(moho, layer)
		if self.transformOrigin then
			local dif = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			local originGlobal = self:GetGlobalPos(moho, layer, layer:Origin())
			if direction then
				dif:Set(center.x - originGlobal.x, 0)
				newPos:Set(center.x + dif.x, originGlobal.y)
			else
				dif:Set(0, center.y - originGlobal.y)
				newPos:Set(originGlobal.x, center.y + dif.y )
			end
			newPos = self:GetLocalPos(moho, layer, newPos)
			self:SetOrigin(moho, layer, newPos)
		end
		 ::continue_to_next_layer::
	end
end

function MR_TransformRigTool:FlipControlHandlesInActions(moho, layer)
	local mesh = layer:Mesh()
	if mesh == nil then
		return
	end

	for j = 0, mesh:CountPoints() - 1 do
		local point = mesh:Point(j)
		if point.fSelected then
			for c = 0, point:CountCurves() - 1 do
				local curve, where = point:Curve(c)
				local offsetChannel1 = AE_Utilities:GetOffsetChannel(moho, layer, curve, where, false)
				for actionID = 0, offsetChannel1:CountActions() - 1 do
					local offsetActionChannel1 = offsetChannel1:Action(actionID)
					local actionName = offsetChannel1:ActionName(actionID)
					for keyID = 0, offsetActionChannel1:CountKeys() - 1 do
						local channelFrame = offsetActionChannel1:GetKeyWhen(keyID)
						if (channelFrame > 0) then
							layer:ActivateAction(actionName)
							curve:SetOffset(where, -curve:GetOffset(where, channelFrame, true), channelFrame, true)
							curve:SetOffset(where, -curve:GetOffset(where, channelFrame, false), channelFrame, false)
							self:CollectLog(nil, actionName)
						end
					end	
				end
				layer:ActivateAction(nil)
			end
		end
	end
end

function MR_TransformRigTool:GetGlobalPos(moho, layer, pos)
	local globalPos = LM.Vector2:new_local()
	globalPos:Set(pos)
	local layerMatrix = LM.Matrix:new_local()
	layer:GetFullTransform(moho.frame, layerMatrix, nil)
	layerMatrix:Transform(globalPos)
	return globalPos
end

function MR_TransformRigTool:GetGlobalBonePos(moho, bone, rest)
	local skel = self.skel
	if skel == nil then
		return
	end

	local selBonePos = LM.Vector2:new_local()
	
	selBonePos:Set(bone.fAnimPos:GetValue(moho.frame))
	
	if bone.fParent >-1 then
		local selBoneParentMatrix = LM.Matrix:new_local()
		if rest then
			selBoneParentMatrix:Set(skel:Bone(bone.fParent).fRestMatrix)
		else
			selBoneParentMatrix:Set(skel:Bone(bone.fParent).fMovedMatrix)
		end
		selBoneParentMatrix:Transform(selBonePos)
	end
	local skelLayerMatrix = LM.Matrix:new_local()
	self.skelLayer:GetFullTransform(moho.frame, skelLayerMatrix, nil)
	skelLayerMatrix:Transform(selBonePos)
	return selBonePos
end

function MR_TransformRigTool:GetLocalPos(moho, layer, globalPos)
	local localPos = LM.Vector2:new_local()
	localPos:Set(globalPos)
	local selLayer = layer
	local selLayerMatrix = LM.Matrix:new_local()
	selLayer:GetFullTransform(0, selLayerMatrix, nil)
	selLayerMatrix:Invert()
	selLayerMatrix:Transform(localPos)
	return localPos
end

function MR_TransformRigTool:GetLocalBonePos(moho, layer, globalPos, bone)
	local skel = self.skel
	local localPos = LM.Vector2:new_local()
	localPos:Set(globalPos)
	
	local selLayer = layer
	local selLayerMatrix = LM.Matrix:new_local()
	selLayer:GetFullTransform(0, 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_TransformRigTool:SetFromSelection(moho)
	self:ScanLayers(moho)
	self:ScanMeshBounds(moho)
	self:SelectedMeshBounds(moho, moho.layer, 0, view)
end

function MR_TransformRigTool:ScanMeshBounds(moho)
	local min = LM.Vector2:new_local()
	local max = LM.Vector2:new_local()
	min:Set(10000,10000) 
	max:Set(-10000,-10000) 
	if self.layersFound == 1 and moho.document:LayerByAbsoluteID(self.vectorLayersToChange.layer[1]) ~= moho.layer then
		self.bMin:Set(0,0)
		self.bMax:Set(0,0)
		return
	end
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local vectorLayer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local mesh = vectorLayer:Mesh()
		local minB = LM.Vector2:new_local()
		local maxB = LM.Vector2:new_local()
		local testBbox = mesh:SelectedBounds(minB, maxB)

		local m = LM.Matrix:new_local()
		local rightUp = LM.Vector2:new_local()
		local rightDown = LM.Vector2:new_local()
		local leftUp = LM.Vector2:new_local()
		local leftDown = LM.Vector2:new_local()
		local allVectors = {}
		rightUp:Set(maxB.x, maxB.y)
		rightDown:Set(maxB.x, minB.y)
		leftUp:Set(minB.x, maxB.y)
		leftDown:Set(minB.x, minB.y)
		vectorLayer:GetFullTransform(0, m, nil)
		m:Transform(rightUp)
		m:Transform(rightDown)
		m:Transform(leftUp)
		m:Transform(leftDown)
		table.insert(allVectors, rightUp)
		table.insert(allVectors, rightDown)
		table.insert(allVectors, leftUp)
		table.insert(allVectors, leftDown)
		for k, vec in ipairs(allVectors) do
			if vec.x < min.x then 
				min.x = vec.x
			end
			if vec.x > max.x then 
				max.x = vec.x
			end
			if vec.y < min.y then 
				min.y = vec.y
			end
			if vec.y > max.y then 
				max.y = vec.y
			end
		end
	end
	self.bMin = min
	self.bMax = max
end

function MR_TransformRigTool:SelectedMeshBounds(moho, layer, frame, view)
	local bbox = LM.BBox:new_local()
	local min = LM.Vector2:new_local()
	local max = LM.Vector2:new_local()
	min:Set(self.bMin)
	max:Set(self.bMax)
	bbox.fMin:Set(min.x, min.y, 0)
	bbox.fMax:Set(max.x, max.y, 0)
	local xLen = bbox.fMax.x - bbox.fMin.x
	local yLen = bbox.fMax.y - bbox.fMin.y
	if (xLen < yLen / 10.0) then
		local center = (bbox.fMin.x + bbox.fMax.x) / 2.0
		bbox.fMin.x = center - yLen / 10.0
		bbox.fMax.x = center + yLen / 10.0
	elseif (yLen < xLen / 10.0) then
		local center = (bbox.fMin.y + bbox.fMax.y) / 2.0
		bbox.fMin.y = center - xLen / 10.0
		bbox.fMax.y = center + xLen / 10.0
	end
	local minLength = 150
	local m = LM.Matrix:new_local()
	local v = LM.Vector2:new_local()
	local pt1 = LM.Point:new_local()
	local pt2 = LM.Point:new_local()

	layer:GetFullTransform(frame, m, moho.document)
	v:Set(bbox.fMin.x, bbox.fMin.y)
	if (self.layersFound == 1) then
		m:Transform(v)
	end
	moho.view:Graphics():WorldToScreen(v, pt1)
	v:Set(bbox.fMax.x, bbox.fMax.y)
	if (self.layersFound == 1) then
		m:Transform(v)
	end	
	moho.view:Graphics():WorldToScreen(v, pt2)
	pt1 = pt2 - pt1
	local length = math.sqrt(pt1.x * pt1.x + pt1.y * pt1.y)
	if (length < minLength) then
		center = (bbox.fMin + bbox.fMax) / 2.0
		v = bbox.fMax - center
		bbox.fMax = center + v * (minLength / length) / 2.0
		v = bbox.fMin - center
		bbox.fMin = center + v * (minLength / length) / 2.0
	end
	bbox:Normalize()
	self.centerVec:Set(bbox:Center2D())
end

function MR_TransformRigTool:ScanGroupForReferences(moho, group)
	for i, l in ipairs(self.refLayersList) do
		self.refLayersList[i] = nil
	end
	local count = 0
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		if layer then
			count = count + 1
			if group:IsLayerValid(layer) then
				if moho:LayerAsVector(layer) and layer:IsReferencedLayer() then
					table.insert(self.refLayersList, layer)
				end
			end
		end
	until not layer	
end

function MR_TransformRigTool:UpdateRefereceLayersInGroup(moho, group)
	local groupLayer = moho:LayerAsGroup(group)
	if groupLayer then
		for i=0, groupLayer:CountLayers()-1 do
			local layer = groupLayer:Layer(i)
			if moho:LayerAsVector(layer) and layer:IsReferencedLayer() then
				layer:MarkReferenceOutdated()
			elseif layer:IsGroupType() then
				self:UpdateRefereceLayersInGroup(moho, layer)
			end
		end	
	end	
end

function MR_TransformRigTool:ScaleBones(moho, mode, scaleFactor)
	local skel = self.skel
	if mode == 1 then -- hierarchy
		local startBone = skel:Bone(skel:SelectedBoneID())
		startBone.fLength = startBone.fTempLength * scaleFactor.x
		for i, a in pairs(self.childBonesList) do
			local bone = a
			local newPos = LM.Vector2:new_local()
			newPos:Set(bone.fTempPos.x * scaleFactor.x, bone.fTempPos.y * scaleFactor.x)
			bone.fLength = bone.fTempLength * scaleFactor.x
			bone.fAnimPos:SetValue(0, newPos)
		end
	end
end

function MR_TransformRigTool:ScanBonesRecursion(moho, bone)
	local skel = self.skel
	local startBone = bone
	
	for i = 0, skel:CountBones() - 1 do
		local bone = skel:Bone(i)
		if bone.fParent == skel:BoneID(startBone) then
			bone.fTempLength = bone.fLength
			bone.fTempPos = bone.fPos
			table.insert(self.childBonesList, bone)
			self:ScanBonesRecursion(moho, bone)
		end
	end
end

function MR_TransformRigTool:SetCenterFromBone(moho)
	self.skel = self:FindSkeleton(moho)
	if self.skel == nil then
		return
	end
	local skel = self.skel
	local selectedBones = self:CountSelectedBones(moho, skel)
	if selectedBones ~= 1 then
		return
	end
	
	local startBone = skel:Bone(skel:SelectedBoneID())
	local boneCenter = LM.Vector2:new_local()
	boneCenter:Set(self:GetGlobalBonePos(moho, startBone))
	self.centerVec:Set(boneCenter)
end

function MR_TransformRigTool:RepeatTransformation(moho, alt, vBones)
	local vBones = vBones or false
	if self.lastTransformMode == 0 then
		return false
	end
	self.skel = self:FindSkeleton(moho)
	if self.skel == nil then
		return false
	end
	local skel = self.skel
	
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, skel)

	if self.transformBones and self.mainSkelSelectedBones ~= 1 then
		return false
	end
	self:FindSkeletonLayer(moho)
	
	local isMarker = self:CheckMarker(moho)
	if self.ignoreRefLayers then
		self:ScanGroupForReferences(moho, self.skelLayer)
	end
	local isStartBoneParent
	local startBone
	if self.transformBones then
		isStartBoneParent = false
		if self.mainSkelSelectedBones > 0 then
			startBone = self.skel:Bone(self.skel:SelectedBoneID())
			if startBone.fParent < 0 then
				isStartBoneParent = false
			else
				isStartBoneParent = true
			end
		end	
	end
	if self.lastTransformMode == 1 then -- translate
		if not vBones then
			moho.document:PrepUndo(self.skelLayer)
			moho.document:SetDirty()
		end	
		
		local offset = LM.Vector2:new_local()
		offset:Set(self.lastTranslate)
		if alt then
			offset:Set(-offset.x, -offset.y)
		end
		
		local bone = startBone
		if not vBones then
			if self.transformPoints then 
				for k, id in ipairs(self.vectorLayersToChange.layer) do
					local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
					local mesh = layer:Mesh()
					local localOffset = LM.Vector2:new_local()	
					local zeroOffset = LM.Vector2:new_local()	
					localOffset:Set(offset)
					localOffset:Set(self:GetLocalPos(moho, layer, localOffset))
					zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
					localOffset:Set(localOffset - zeroOffset)
					mesh:TranslatePoints(localOffset)
					moho:AddPointKeyframe(0, layer)
				end
				self:TranslatePointsInActions(moho, offset)
			end
			
			if self.transformOrigin and not vBones then
				self:TranslateOrigins(moho, offset)
			end
			
			if self.transformImageLayers then
				for i, id in ipairs(self.imageLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:TranslateLayer(moho, layer, offset)
				end
			end
			if self.transformPatchLayers then
				for i, id in ipairs(self.patchLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:TranslateLayer(moho, layer, self.offset)
				end	
			end
		end
		if self.transformBones and self.mainSkelSelectedBones == 1 then
			local localOffset = LM.Vector2:new_local()
			local zeroOffset = LM.Vector2:new_local()
			zeroOffset:Set(0, 0)
			localOffset:Set(self:GetLocalPos(moho, self.skelLayer, offset))
			zeroOffset:Set(self:GetLocalPos(moho, self.skelLayer, zeroOffset))
			localOffset:Set(localOffset - zeroOffset)
			if bone.fParent >= 0 then
				local bonePos = LM.Vector2:new_local()		
				bonePos:Set(bone.fAnimPos:GetValue(0))
				local parentBone = skel:Bone(bone.fAnimParent:GetValue(0))
				local parentMatrix = parentBone.fRestMatrix
				parentMatrix:Transform(bonePos)
				bonePos:Set(bonePos + localOffset)
				local invertedMatrix = LM.Matrix:new_local()
				invertedMatrix:Set(parentMatrix)
				invertedMatrix:Invert()
				local newPos = LM.Vector2:new_local()
				newPos:Set(bonePos)
				invertedMatrix:Transform(newPos)
				bone.fAnimPos:SetValue(0, newPos)
			else
				bone.fAnimPos:SetValue(0, bone.fAnimPos:GetValue(0) + localOffset)
			end
			if not isMarker then
				self:TranslateBoneInActions(moho, bone, offset)
			end	
		end
	elseif self.lastTransformMode >= 2 and self.lastTransformMode <= 4 then -- scale
		if not vBones then
			moho.document:PrepUndo(self.skelLayer)
			moho.document:SetDirty()
		end	
		local skel = self.skel
		
		local scaling = LM.Vector2:new_local()
		scaling:Set(self.lastScaling)
		if self.transformBones and scaling.x ~= scaling.y then
			self.status = self:Localize('Non uniform scaling not supported for bones.')
			self:UpdateWidgets(moho)
			return false
		end
		
		if alt then
			local scalePercentageX = 100 / (1 / scaling.x)
			local scalePercentageY = 100 / (1 / scaling.y)
			scaling:Set(1/((1/100) * scalePercentageX), 1/((1/100) * scalePercentageY))
		end
		
		if scaling.x == scaling.y then
			self.scalingMode = 1
		elseif scaling.x ~= 1 and scaling.y == 1 then
			self.scalingMode = 2
		elseif scaling.x == 1 and scaling.y ~= 1 then
			self.scalingMode = 3
		elseif scaling.x ~= scaling.y and scaling.x ~= 1 and scaling.y ~= 1 then
			self.scalingMode = 4
		end

		if self.mainSkelSelectedBones > 1 and self.centerId == 1 then
			return false
		end
		local boneCenter = LM.Vector2:new_local()
		if self.centerId == 1 and startBone then
			boneCenter:Set(self:GetGlobalBonePos(moho, startBone, true))
		end	
		
		if self.transformBones and self.mainSkelSelectedBones == 1 then
			if self.lastTransformMode == 2 and scaling.x == scaling.y then -- scale
				local startBonePos = LM.Vector2:new_local()
				local centerVec = LM.Vector2:new_local()
				local newPos = LM.Vector2:new_local()
				local newPosInLayer = LM.Vector2:new_local()
				local dif = LM.Vector2:new_local()
				startBone.fTempLength = startBone.fLength
				if startBone.fParent < 0 then
					startBonePos:Set(startBone.fAnimPos:GetValue(0))
					centerVec:Set(self.lastCenter)
					centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
					dif:Set(startBonePos - centerVec)
					newPos:Set((dif * scaling.x) + centerVec)
					newPosInLayer:Set(newPos)
				else
					local parentBoneMatrix = LM.Matrix:new_local()
					local parentBone = skel:Bone(startBone.fParent)
					startBonePos:Set(startBone.fAnimPos:GetValue(0))
					parentBoneMatrix = parentBone.fMovedMatrix
					parentBoneMatrix:Transform(startBonePos)
					centerVec:Set(self.lastCenter)
					centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
					dif:Set(startBonePos - centerVec)
					newPos:Set((dif * scaling.x) + centerVec)
					newPosInLayer:Set(newPos)
					parentBoneMatrix:Invert()
					parentBoneMatrix:Transform(newPos)
				end
					startBone.fAnimPos:SetValue(0, newPos)
				
				if not isMarker then
					if self.adaptiveScaleAndRotation then
						self:CorrectScalingOffsetInActions(moho, startBone, self.lastCenter, scaling)
					else	
						local offset = LM.Vector2:new_local()
						offset:Set(self:GetGlobalPos(moho, self.skelLayer, newPosInLayer) - self:GetGlobalPos(moho, self.skelLayer, startBonePos))
						
						self:TranslateBoneInActions(moho, startBone, offset)
					end	
				
				end		
				for i, l in ipairs(self.childBonesList) do
					self.childBonesList[i] = nil
				end
				
				for i = 0, skel:CountBones() - 1 do
					local bone = skel:Bone(i)
					if bone.fParent == skel:BoneID(startBone) then
						bone.fTempLength = bone.fLength
						bone.fTempPos = bone.fPos
						table.insert(self.childBonesList, bone)
						self:ScanBonesRecursion(moho, bone)
					end
				end
				if self.transformBones and scaling.x == scaling.y and self.mainSkelSelectedBones == 1 then
					self:ScaleBones(moho, 1, scaling)
				end
				if isStartBoneParent then
					self:ScaleBonesInActions(moho, boneCenter, scaling)
				else
					self:ScaleBonesInActions(moho, self.lastCenter, scaling)
				end
			end
		end
		if not vBones then
			if self.transformPoints then 
				for k, id in ipairs(self.vectorLayersToChange.layer) do
					local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
					local mesh = layer:Mesh()
					local localCenter = LM.Vector2:new_local()
					-- print(scaling.x..'  '..scaling.y)
					localCenter:Set(self.lastCenter)
					localCenter:Set(self:GetLocalPos(moho, layer, localCenter)) -- use global pos
					mesh:ScalePoints(scaling.x, scaling.y, localCenter)
					if self.isFlip then
						for i = 0, mesh:CountPoints() - 1 do
							local pt = mesh:Point(i)
							pt:FlipControlHandles(0)
						end	
					end

					moho:AddPointKeyframe(0, layer)
				end	
				self:ScalePointsInActions(moho, self.lastCenter, scaling)
				if self.adjustStrokeWidth and scaling.x == scaling.y then
					self:AdjustStrokesWidth(moho, scaling.x)
				end	
			end
			if self.transformOrigin then
				self:ScaleOrigins(moho, self.lastCenter, scaling)
			end
			
			if self.transformImageLayers then
				for i, id in ipairs(self.imageLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					local localCenter = LM.Vector2:new_local()
					localCenter:Set(self.lastCenter)
					self:ScaleLayer(moho, layer, localCenter, scaling)
				end
			end
			if self.transformPatchLayers then
				for i, id in ipairs(self.patchLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					local localCenter = LM.Vector2:new_local()
					localCenter:Set(self.lastCenter)
					self:ScaleLayer(moho, layer, localCenter, scaling)
				end	
			end
		end
	elseif self.lastTransformMode == 5 then -- rotate
		if not vBones then
			moho.document:PrepUndo(self.skelLayer)
			moho.document:SetDirty()
		end	
		local skel = self.skel
		local angle = self.lastAngle
		local center = LM.Vector2:new_local()
		center:Set(self.lastCenter)
		if alt then
			angle = -angle
		end
		
		if self.mainSkelSelectedBones > 1 and self.centerId == 1 then
			return
		end

		if not vBones then
			if self.transformPoints then 
				for k, id in ipairs(self.vectorLayersToChange.layer) do
					local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
					local mesh = layer:Mesh()
					local localAngle = angle
					if layer:GetFollowingLayer() == nil and self.followPathAdaptation or not self.followPathAdaptation then
						if self.vectorLayersToChange.parentalFlip[k] then
							localAngle = -localAngle
						end
						local localCenter = LM.Vector2:new_local()
						localCenter:Set(center)
						localCenter:Set(self:GetLocalPos(moho, layer, localCenter)) -- use global pos
						
						mesh:RotatePoints(localAngle, localCenter)
						moho:AddPointKeyframe(0, layer)
					end	
				end
				self:RotatePointsInActions(moho, center, angle)
			end	
			if self.transformOrigin then
				self:RotateOrigins(moho, center, angle)
			end
			
			if self.transformImageLayers then
				for i, id in ipairs(self.imageLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:RotateLayer(moho, layer, center, angle)
				end
			end
			if self.transformPatchLayers then
				for i, id in ipairs(self.patchLayersToChange.layer) do
					local layer = moho.document:LayerByAbsoluteID(id)
					self:RotateLayer(moho, layer, center, angle)
				end	
			end
		end	
		if self.transformBones and startBone then
			if self:CheckLayerParentalFlip(moho, self.skelLayer, true) then
				angle = -angle
			end
			startBone.fAnimAngle:SetValue(0, startBone.fAnimAngle:GetValue(0) + angle)
			
			local startBonePos = LM.Vector2:new_local()
			local centerVec = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			local newPosInLayer = LM.Vector2:new_local()
			local dif = LM.Vector2:new_local()
			startBonePos:Set(startBone.fAnimPos:GetValue(0))
			centerVec:Set(center)
			centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
			if startBone.fParent < 0 then
				dif:Set(startBonePos - centerVec)
				local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
				local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
				newPos:Set(px + centerVec.x, py + centerVec.y)
				newPosInLayer:Set(newPos)
			else	
				local parentBoneMatrix = LM.Matrix:new_local()
				local parentBone = skel:Bone(startBone.fParent)
				parentBoneMatrix = parentBone.fRestMatrix
				parentBoneMatrix:Transform(startBonePos)
				dif:Set(startBonePos - centerVec)
				local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
				local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
				newPos:Set(px + centerVec.x, py + centerVec.y)
				newPosInLayer:Set(newPos)
				parentBoneMatrix:Invert()
				parentBoneMatrix:Transform(newPos)
			end
			startBone.fAnimPos:SetValue(0, newPos)
			
			if not isMarker then
				if self.adaptiveScaleAndRotation then
					self:CorrectRotatingOffsetInActions(moho, startBone, self.lastCenter, angle)
				else	
					local offset = LM.Vector2:new_local()
					offset:Set(self:GetGlobalPos(moho, self.skelLayer, newPosInLayer) - self:GetGlobalPos(moho, self.skelLayer, startBonePos))
					self:TranslateBoneInActions(moho, startBone, offset)
				end
				self:RotateBoneInActions(moho, startBone, angle)
			end	
		end
	end
	if self.ignoreRefLayers then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end	
	return true
end

function MR_TransformRigTool:RotatePointsInActions(moho, center, angle)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local localAngle = angle
		if layer:GetFollowingLayer() == nil and self.followPathAdaptation or not self.followPathAdaptation then
			if self.vectorLayersToChange.parentalFlip[k] then
				localAngle = -localAngle
			end
			local mesh = layer:Mesh()
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			centerVec = self:GetLocalPos(moho, layer, centerVec) --use 
			local m = LM.Matrix:new_local()
			local mInverted = LM.Matrix:new_local()
			layer:GetFullTransform(moho.frame, m, nil)
			mInverted:Set(m)
			mInverted:Invert()
			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				if pt.fSelected then
					for actionID = 0, pt.fAnimPos:CountActions() - 1 do
						local action = moho:ChannelAsAnimVec2(pt.fAnimPos:Action(actionID))
						local actionName = pt.fAnimPos:ActionName(actionID)
						if action ~= nil then
							for keyID = 0, action:CountKeys() - 1 do
								local posFrame = action:GetKeyWhen(keyID)
								if (posFrame > 0) then
									local dif = LM.Vector2:new_local()
									local originalPos = LM.Vector2:new_local()
									local newPos = LM.Vector2:new_local()
									local pointPos = action:GetValue(posFrame)
									originalPos:Set(pointPos)
									dif:Set(pointPos - centerVec)
									local px = dif.x * math.cos(localAngle) - dif.y * math.sin(localAngle)
									local py = dif.x * math.sin(localAngle) + dif.y * math.cos(localAngle)
									newPos:Set(px + centerVec.x, py + centerVec.y)
									action:SetValue(posFrame, newPos)
									if originalPos ~= newPos then
										self:CollectLog(nil, actionName)
									end	
								end
							end
						end	
					end
				end	
			end
			if self.isFlip then
				self:FlipControlHandlesInActions(moho, layer)
			end	
		end	
	end	
end
function MR_TransformRigTool:TranslatePointsInActions(moho, offset)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local mesh = layer:Mesh()
		local localOffset = LM.Vector2:new_local()
		local zeroOffset = LM.Vector2:new_local()
		localOffset:Set(offset)
		localOffset:Set(self:GetLocalPos(moho, layer, localOffset))
		zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
		localOffset:Set(localOffset - zeroOffset)
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if pt.fSelected then
				for actionID = 0, pt.fAnimPos:CountActions() - 1 do
					local action = moho:ChannelAsAnimVec2(pt.fAnimPos:Action(actionID))
					local actionName = pt.fAnimPos:ActionName(actionID)
					if action ~= nil then
						for keyID = 0, action:CountKeys() - 1 do
							local posFrame = action:GetKeyWhen(keyID)
							if (posFrame > 0) then
								local originalPos = action:GetValue(posFrame)
								local newPos = originalPos + localOffset
								action:SetValue(posFrame, newPos)
								if originalPos ~= newPos then
									self:CollectLog(nil, actionName)
								end	
							end
						end
					end	
				end
			end	
		end
	end	
end

function MR_TransformRigTool:ScalePointsInActions(moho, centerScale, scaleValue)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local mesh = layer:Mesh()
		local centerVec = LM.Vector2:new_local()
		centerVec:Set(centerScale)
		centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if pt.fSelected then
				for actionID = 0, pt.fAnimPos:CountActions() - 1 do
					local action = moho:ChannelAsAnimVec2(pt.fAnimPos:Action(actionID))
					local actionName = pt.fAnimPos:ActionName(actionID)
					if action ~= nil then
						for keyID = 0, action:CountKeys() - 1 do
							local posFrame = action:GetKeyWhen(keyID)
							if (posFrame > 0) then
								local dif = LM.Vector2:new_local()
								local newPos = LM.Vector2:new_local()
								local originalPos = LM.Vector2:new_local()
								local pointPos = action:GetValue(posFrame)
								originalPos:Set(pointPos)
								dif:Set(pointPos - centerVec)
								if self.scalingMode == 1 then -- uniform
									newPos:Set((dif * scaleValue.x) + centerVec)
								elseif self.scalingMode == 2 then -- h
									newPos:Set((dif.x * scaleValue.x) + centerVec.x, pointPos.y )
								elseif self.scalingMode == 3 then -- v
									newPos:Set(pointPos.x,(dif.y * scaleValue.y) + centerVec.y)
								elseif self.scalingMode == 4 then
									newPos:Set((dif.x * scaleValue.x) + centerVec.x, (dif.y * scaleValue.y) + centerVec.y)
								end	
								action:SetValue(posFrame, newPos)
								if originalPos ~= newPos then
									self:CollectLog(nil, actionName)
								end	
							end
						end
					end	
				end
			end	
		end
		if self.isFlip then
			self:FlipControlHandlesInActions(moho, layer)
		end	
	end	
end

function MR_TransformRigTool:ScaleBonesInActions(moho, centerScale, scaleValue)
	local skel = self.skel
	if 1 == 1 then -- hierarchy
		local startBone = skel:Bone(skel:SelectedBoneID())
		local centerVec = LM.Vector2:new_local()
		centerVec:Set(centerScale)
		centerVec = self:GetLocalPos(moho, self.skelLayer, centerVec) --use global pos
		
		for i, a in pairs(self.childBonesList) do
			local bone = a
			for actionID = 0, bone.fAnimPos:CountActions() - 1 do
				local action = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
				local actionName = bone.fAnimPos:ActionName(actionID)
				if action ~= nil then
					for keyID = 0, action:CountKeys() - 1 do
						local posFrame = action:GetKeyWhen(keyID)
						if (posFrame > 0) then
							local newPos = LM.Vector2:new_local()
							local bonePos = action:GetValue(posFrame)
							newPos:Set(bonePos.x * scaleValue.x, bonePos.y * scaleValue.x)
							action:SetValue(posFrame, newPos)
							if bonePos ~= newPos then
								self:CollectLog(nil, actionName)
							end	
						end	
					end		
				end			
			end	
		end
	end
end

function MR_TransformRigTool:UpdateLastCenter(moho)
	if self.centerId == 0 then
		self.lastCenter = self.centerVec
	elseif self.centerId == 1 then
		self.skel = self:FindSkeleton(moho)
		if self.skel == nil then
			return
		end
		local selectedBones = self:CountSelectedBones(moho, self.skel)
		if selectedBones == 1 then
			local boneCenter = LM.Vector2:new_local()
			local startBone = self.skel:Bone(self.skel:SelectedBoneID())
			boneCenter:Set(self:GetGlobalBonePos(moho, startBone))
			self.lastCenter:Set(boneCenter)
		end	
	end
end

function MR_TransformRigTool:FindSkeletonLayer(moho)
	local skelLayer = nil
	if moho:Skeleton() and moho:Skeleton():CountBones() > 0 then
		skelLayer = moho.layer
	elseif moho.layer:ControllingBoneLayer() then
		skelLayer = moho.layer:ControllingBoneLayer()
	end
	if skelLayer == nil then
		local targetSkelLayer = nil
		local parentGroup = moho.layer:Parent()
		if parentGroup ~= nil then
			local targetGroup = parentGroup
			repeat
				if targetGroup:ControllingBoneLayer() then
					targetSkelLayer = targetGroup:ControllingBoneLayer()
					if targetSkelLayer ~= nil then
						skelLayer = targetSkelLayer
						break
					end
				end
				targetGroup = targetGroup:Parent()
			until targetGroup == nil 
		end
	end
	self.skelLayer = skelLayer
end

function MR_TransformRigTool:FindSkeleton(moho)
	self.isSkel = true
	local skel = moho:Skeleton()
	if skel ~= nil and skel:CountBones() < 1 then
		skel = moho.layer:ControllingSkeleton()
	elseif (not moho:LayerAsBone(moho.layer)) then
		skel = moho.layer:ControllingSkeleton()
	end
	if skel == nil then
		local targetSkel = nil
		local parentGroup = moho.layer:Parent()
		if parentGroup ~= nil then
			local targetGroup = parentGroup
			repeat
				if targetGroup:ControllingSkeleton() then
					targetSkel = targetGroup:ControllingSkeleton()
					if targetSkel ~= nil then
						if targetSkel:CountBones() >0 then
							skel = targetSkel
							self.isSkel = true
							return skel
						end	
					end
				end
				targetGroup = targetGroup:Parent()
			until targetGroup == nil 
			self.isSkel = false
		end
	else
		self.isSkel = true
		return skel
	end
end

function MR_TransformRigTool:CountSelectedBones(moho, skeleton) --???
	if not skeleton then
		return 0
	end
	local selectedBones = 0
	for b=0, skeleton:CountBones() -1 do
		local myBone = skeleton:Bone(b)
		if myBone.fSelected then
			selectedBones = selectedBones +1
		end	
	end
	return selectedBones
end

function MR_TransformRigTool:CollectPointsTempPos(moho)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local mesh = layer:Mesh()
		mesh:PrepMovePoints()
		if layer:AncestorSwitchLayer() then
			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				if pt.fSelected then
					pt.fTempPos = pt.fAnimPos:GetValue(0)
				end	
			end
		end	
	end	
end

function MR_TransformRigTool:Round(x,n)
	n = math.pow(10, n or 4)
	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_TransformRigTool:GetDistance(Pos1, Pos2)
	return math.sqrt((Pos2.x-Pos1.x)^2+(Pos2.y-Pos1.y)^2)
end

function MR_TransformRigTool:FlipBoneChain(moho, direction, smartBone, alt)
	local skel = self.skel
	if self.skel == nil then
		return
	end
	local startBone = skel:Bone(skel:SelectedBoneID())
	
	for i, l in ipairs(self.childBonesList) do
		self.childBonesList[i] = nil
	end
	self:ScanBonesRecursion(moho, startBone)
	for i, a in pairs(self.childBonesList) do
		local bone = a
		bone.fTempAngle = bone.fAnimAngle:GetValue(0)
	end	
	startBone.fTempAngle = startBone.fAnimAngle:GetValue(0)
	skel:FlipBone(skel:BoneID(startBone), false)
	startBone.fMinConstraint, startBone.fMaxConstraint = -startBone.fMaxConstraint, -startBone.fMinConstraint
	local skelMatrix = LM.Matrix:new_local()
	self.skelLayer:GetFullTransform(0, skelMatrix, nil)
	local lastSkelMatrix = LM.Matrix:new_local()
	local invertedLastSkelMatrix = LM.Matrix:new_local()
	if self.lastSkelLayer ~= nil then
		self.lastSkelLayer:GetFullTransform(0, lastSkelMatrix, nil)
		invertedLastSkelMatrix:Set(lastSkelMatrix)
		invertedLastSkelMatrix:Invert()
	end
	local invertedSkelMatrix = LM.Matrix:new_local()
	invertedSkelMatrix:Set(skelMatrix)
	invertedSkelMatrix:Invert()
	
	if startBone.fParent < 0 then
		if alt or self.useGlobalFlip then
			local boneMatrix = startBone.fMovedMatrix
			local invertedBoneMatrix = LM.Matrix:new_local()										
			invertedBoneMatrix:Set(boneMatrix)
			invertedBoneMatrix:Invert()
			local v1 = LM.Vector2:new_local()
			local v2 = LM.Vector2:new_local()
			v1:Set(startBone.fAnimPos:GetValue(0))
			if (startBone:IsZeroLength()) then
				v2:Set(0.1, 0)
			else
				v2:Set(startBone.fLength, 0)
			end
			boneMatrix:Transform(v2)
			
			skelMatrix:Transform(v1)
			skelMatrix:Transform(v2)
			
			if alt and not self.useGlobalFlip then
				invertedLastSkelMatrix:Transform(v1)
				invertedLastSkelMatrix:Transform(v2)
			end
			
			if direction then
				v2:Set(v1.x + (v1.x - v2.x),v2.y)
			else
				v2:Set(v2.x, v1.y + (v1.y - v2.y))
			end	

			if alt and not self.useGlobalFlip then
				lastSkelMatrix:Transform(v1)
				lastSkelMatrix:Transform(v2)
			end

			invertedSkelMatrix:Transform(v1)
			invertedSkelMatrix:Transform(v2)
			
			local newAngle = 0
			v2 = v2 - v1
			newAngle = math.atan2(v2.y, v2.x)
			while newAngle > math.rad(360) do
				newAngle = newAngle - math.rad(360)
			end
			while newAngle < - math.rad(360) do
				newAngle = newAngle + math.rad(360)
			end	
			startBone.fAnimAngle:SetValue(0, newAngle)
		else
			if direction then
				startBone.fAnimAngle:SetValue(0, math.rad(180) - startBone.fAnimAngle:GetValue(0))
			else
				startBone.fAnimAngle:SetValue(0, -startBone.fAnimAngle:GetValue(0))
			end	
		end
	else
		local parent = skel:Bone(startBone.fParent)
		local parentMatrix = parent.fMovedMatrix
		local invMatrix = LM.Matrix:new_local()										
		invMatrix:Set(parentMatrix)
		invMatrix:Invert()
		local v1 = LM.Vector2:new_local()
		local v2 = LM.Vector2:new_local()
		v1:Set(startBone.fAnimPos:GetValue(0))
		if (startBone:IsZeroLength()) then
			v2:Set(0.1, 0)
		else
			v2:Set(startBone.fLength, 0)
		end
		parentMatrix:Transform(v1)
		startBone.fMovedMatrix:Transform(v2)
		
		if alt and not self.useGlobalFlip then
			skelMatrix:Transform(v1)
			skelMatrix:Transform(v2)
			invertedLastSkelMatrix:Transform(v1)
			invertedLastSkelMatrix:Transform(v2)
		elseif self.useGlobalFlip then
			skelMatrix:Transform(v1)
			skelMatrix:Transform(v2)
		end
		
		if direction then
			v2:Set(v1.x + (v1.x - v2.x),v2.y)
		else
			v2:Set(v2.x, v1.y + (v1.y - v2.y))
		end	
		
		if alt and not self.useGlobalFlip then
			lastSkelMatrix:Transform(v1)
			lastSkelMatrix:Transform(v2)
			invertedSkelMatrix:Transform(v1)
			invertedSkelMatrix:Transform(v2)
		elseif self.useGlobalFlip then
			invertedSkelMatrix:Transform(v1)
			invertedSkelMatrix:Transform(v2)
		end
		
		invMatrix:Transform(v1)
		invMatrix:Transform(v2)
		
		local newAngle = 0
		v2 = v2 - v1
		newAngle = math.atan2(v2.y, v2.x)
		while newAngle > math.rad(360) do
			newAngle = newAngle - math.rad(360)
		end
		while newAngle < - math.rad(360) do
			newAngle = newAngle + math.rad(360)
		end	
		startBone.fAnimAngle:SetValue(0, newAngle)
	end	
	---------- collect angle info ----------
	for i, a in pairs(self.childBonesList) do
		local bone = a
		bone.fTempAngle = bone.fAnimAngle:GetValue(0)
	end	
	for i, l in ipairs(self.bonesList) do
		self.bonesList[i] = nil
	end
	
	for i, a in pairs(self.childBonesList) do
		local bone = a
		local actionsList = {}
		for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
			local actionChannel = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
			local actionKeysList = {}
			for keyID = 0, actionChannel:CountKeys() - 1 do
				local posFrame = actionChannel:GetKeyWhen(keyID)
				table.insert(actionKeysList, actionChannel:GetValue(posFrame))
			end
			table.insert(actionsList, actionKeysList)
		end
		table.insert(self.bonesList, actionsList)	
	end
	---------- correct start bone angle in actions ----------
	for actionID = 0, startBone.fAnimAngle:CountActions() - 1 do
		local actionChannel = moho:ChannelAsAnimVal(startBone.fAnimAngle:Action(actionID))
		local actionName = startBone.fAnimAngle:ActionName(actionID)
		for keyID = 0, actionChannel:CountKeys() - 1 do
			local keyFrame = actionChannel:GetKeyWhen(keyID)
			if (keyFrame > 0) then
				local newAngle = 0
				local originalValue = actionChannel:GetValue(0)
				if startBone.fParent < 0 and not (alt or self.useGlobalFlip) then
					if direction then
						newAngle = math.rad(180) - actionChannel:GetValue(keyFrame)
					else
						newAngle = -actionChannel:GetValue(keyFrame)
					end
				else
					local angleDelta =  startBone.fTempAngle - originalValue
					local startValue = actionChannel:GetValue(keyFrame)
					local angleDif = 0
					if direction then
						angleDif = originalValue - startValue + angleDelta
					else
						angleDif = (originalValue - startValue) + angleDelta
					end
					newAngle = originalValue + angleDif
				end
				actionChannel:SetValue(keyFrame, newAngle)
				if originalValue ~= newAngle then
					self:CollectLog(nil, actionName)
				end	
			end	
		end		
	end	
	
	for i, a in pairs(self.childBonesList) do
		local bone = a
		if bone.fParent ~= skel:BoneID(startBone) then
			local newPos = LM.Vector2:new_local()
			newPos:Set(bone.fAnimPos:GetValue(0))
			newPos:Set(newPos.x, -newPos.y)
			bone.fAnimPos:SetValue(0, newPos)
		end
		
		bone.fMinConstraint, bone.fMaxConstraint = -bone.fMaxConstraint, -bone.fMinConstraint
		
		for actionID = 0, bone.fAnimPos:CountActions() - 1 do
			local actionChannel = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
			local actionName = bone.fAnimPos:ActionName(actionID)
			for keyID = 0, actionChannel:CountKeys() - 1 do
				local posFrame = actionChannel:GetKeyWhen(keyID)
				if (posFrame > 0) then
					local newPos = LM.Vector2:new_local()
					local originalPos = LM.Vector2:new_local()
					originalPos:Set(actionChannel:GetValue(posFrame))
					newPos:Set(originalPos)
					newPos:Set(newPos.x, -newPos.y)
					actionChannel:SetValue(posFrame, newPos)
					if originalPos ~= newPos then
						self:CollectLog(nil, actionName)
					end	
				end	
			end		
		end	
		local maxValue = 0
		for aID = 0, bone.fAnimAngle:CountActions() - 1 do
			local actionChannel = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(aID))
			local actionName = bone.fAnimAngle:ActionName(aID)
			local startValue = actionChannel:GetValue(0)
			local origZeoro = self.bonesList[i][aID + 1][1]
			local origStartValue = bone.fTempAngle
			local oldStartValue = 0
			oldStartValue = math.rad(360) - startValue
			for keyID = 0, actionChannel:CountKeys() - 1 do
				local posFrame = actionChannel:GetKeyWhen(keyID)
				if (posFrame > 0) then
					local oldValue = actionChannel:GetValue(posFrame)
					local oldDelta = oldValue - oldStartValue
					local origCurrent = self.bonesList[i][aID + 1][keyID + 1]
					local origPrew = self.bonesList[i][aID + 1][keyID]
					if math.abs(oldDelta) > math.abs(origPrew - origCurrent) + math.rad(180) then
						if oldDelta < 0 then
							oldDelta = oldDelta + math.rad(360)
						elseif oldDelta > 0 then
							oldDelta = oldDelta - math.rad(360)
						end
					end
					local newValue = startValue - oldDelta
					if math.abs(newValue) > (math.abs(startValue) + math.rad(360)) then
						maxValue = newValue
					end	
					actionChannel:SetValue(posFrame, newValue)
					if oldValue ~= newValue then
						self:CollectLog(nil, actionName)
					end	
				end	
			end
			if math.abs(maxValue) > 0 then
				for keyID = 0, actionChannel:CountKeys() - 1 do
					local posFrame = actionChannel:GetKeyWhen(keyID)
					if (posFrame > 0) then
						local targetValue = maxValue
						local originalValue = actionChannel:GetValue(posFrame)
						local oldValue = actionChannel:GetValue(posFrame)
						local startValue = actionChannel:GetValue(0)
						while targetValue > (math.rad(360) + startValue) do
							oldValue = oldValue - math.rad(360)
							targetValue = targetValue - math.rad(360)
						end
						while targetValue < - (math.rad(360) + startValue) do
							oldValue = oldValue + math.rad(360)
							targetValue = targetValue + math.rad(360)
						end	
						actionChannel:SetValue(posFrame, newValue)
						if originalValue ~= newValue then
							self:CollectLog(nil, actionName)
						end	
					end	
				end	
			end		
		end	
	end	
	---------- correct start bone position in actions ----------
	for actionID = 0, startBone.fAnimPos:CountActions() - 1 do
		moho:SetCurFrame(0)
		local actionChannel = moho:ChannelAsAnimVec2(startBone.fAnimPos:Action(actionID))
		local actionName = startBone.fAnimPos:ActionName(actionID)
		local startPos = LM.Vector2:new_local()
		startPos:Set(startBone.fAnimPos:GetValue(0))
		
		local globalStartPos = LM.Vector2:new_local()
		globalStartPos:Set(startPos)
		if startBone.fParent > -1 then
			skel:Bone(startBone.fParent).fMovedMatrix:Transform(globalStartPos)
		end
		local lastSkelMatrix = LM.Matrix:new_local()
		local skelMatrix = LM.Matrix:new_local()
		self.skelLayer:GetFullTransform(0, skelMatrix, nil)
		local invertedSkelMatrix = LM.Matrix:new_local()
		invertedSkelMatrix:Set(skelMatrix)
		invertedSkelMatrix:Invert()
		
		if self.lastSkelLayer ~= nil then
			self.lastSkelLayer:GetFullTransform(0, lastSkelMatrix, nil)
		end
		
		local invertedLastSkelMatrix = LM.Matrix:new_local()
		invertedLastSkelMatrix:Set(lastSkelMatrix)
		invertedLastSkelMatrix:Invert()
		if alt and not self.useGlobalFlip then
			skelMatrix:Transform(startPos)
			skelMatrix:Transform(globalStartPos)
			invertedLastSkelMatrix:Transform(startPos)
			invertedLastSkelMatrix:Transform(globalStartPos)
		elseif self.useGlobalFlip then
			skelMatrix:Transform(startPos)
			skelMatrix:Transform(globalStartPos)
		end
		for keyID = 0, actionChannel:CountKeys() - 1 do
			local posFrame = actionChannel:GetKeyWhen(keyID)
			if (posFrame > 0) then
				if startBone.fParent < 0 then
					if alt or self.useGlobalFlip then
						self.skelLayer:ActivateAction(actionName)
						moho:SetCurFrame(posFrame)
						local newPos = LM.Vector2:new_local()
						local curPos = LM.Vector2:new_local()
						local difPos = LM.Vector2:new_local()
						local originalPos = LM.Vector2:new_local()
						curPos:Set(actionChannel:GetValue(posFrame))
						originalPos:Set(curPos)
						if alt and not self.useGlobalFlip then
							skelMatrix:Transform(curPos)
							invertedLastSkelMatrix:Transform(curPos)
						elseif self.useGlobalFlip then
							skelMatrix:Transform(curPos)
						end

						difPos:Set(startPos - curPos)
						newPos:Set(startPos + difPos)
						if direction then
							newPos:Set(newPos.x, curPos.y)
						else
							newPos:Set(curPos.x, newPos.y)
						end
					
						if alt and not self.useGlobalFlip then
							lastSkelMatrix:Transform(newPos)
							invertedSkelMatrix:Transform(newPos)
						elseif self.useGlobalFlip then
							invertedSkelMatrix:Transform(newPos)
						end
						actionChannel:SetValue(posFrame, newPos)
					else
						local newPos = LM.Vector2:new_local()
						local difPos = LM.Vector2:new_local()
						newPos:Set(actionChannel:GetValue(posFrame))
						difPos:Set(startPos - newPos)
						if direction then
							newPos:Set(startPos.x + difPos.x, newPos.y)
						else
							newPos:Set(newPos.x, startPos.y + difPos.y)
						end
						actionChannel:SetValue(posFrame, newPos)
						if originalPos ~= newPos then
							self:CollectLog(nil, actionName)
						end	
					end
				else
					self.skelLayer:ActivateAction(actionName)
					moho:SetCurFrame(posFrame)
					local newPos = LM.Vector2:new_local()
					local globalCurPos = LM.Vector2:new_local()
					local difPos = LM.Vector2:new_local()
					local originalPos = LM.Vector2:new_local()
					local parentBoneMatrix = LM.Matrix:new_local()
					parentBoneMatrix:Set(skel:Bone(startBone.fParent).fMovedMatrix)
					originalPos:Set(actionChannel:GetValue(posFrame))
					globalCurPos:Set(originalPos)
					parentBoneMatrix:Transform(globalCurPos)
					
					if alt and not self.useGlobalFlip then
						skelMatrix:Transform(globalCurPos)
						invertedLastSkelMatrix:Transform(globalCurPos)
					elseif self.useGlobalFlip then
						skelMatrix:Transform(globalCurPos)
					end
					
					difPos:Set(globalStartPos - globalCurPos)
					newPos:Set(globalStartPos + difPos)
					if direction then
						newPos:Set(newPos.x, globalCurPos.y)
					else
						newPos:Set(globalCurPos.x, newPos.y)
					end
					
					if alt and not self.useGlobalFlip then
						lastSkelMatrix:Transform(newPos)
						invertedSkelMatrix:Transform(newPos)
					elseif self.useGlobalFlip then
						invertedSkelMatrix:Transform(newPos)
					end
					
					parentBoneMatrix:Invert()
					parentBoneMatrix:Transform(newPos)
					actionChannel:SetValue(posFrame, newPos)
					if originalPos ~= newPos then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end
		self.skelLayer:ActivateAction(nil)
		moho:SetCurFrame(0)
	end
end

function MR_TransformRigTool:IsLayerFlipped(moho, layer, direction)
	local isFlipped = false
	if direction then
		isFlipped = layer.fFlipH.value
	else
		isFlipped = layer.fFlipV.value
	end
	local parentGroup = layer:Parent()
	if parentGroup ~= nil then
		local targetGroup = parentGroup
		repeat
			if direction then
				if targetGroup.fFlipH.value then
					isFlipped = not isFlipped
				end
			else
				if targetGroup.fFlipV.value then
					isFlipped = not isFlipped
				end
			end
			targetGroup = targetGroup:Parent()
		until targetGroup == nil 
	end
	return isFlipped
end

function MR_TransformRigTool:SetOrigin(moho, layer, pos)
	local matrix = LM.Matrix:new_local()
	local beforeVec = LM.Vector3:new_local()
	local afterVec = LM.Vector3:new_local()
	layer:GetLayerTransform(0, matrix, nil)
	matrix:Transform(beforeVec)	
	layer:SetOrigin(pos)
	layer:GetLayerTransform(moho.frame, matrix, nil)
	matrix:Transform(afterVec)	
	local returnVec = beforeVec - afterVec
	if returnVec:Mag() > 0.000001 then
		layer.fTranslation:SetValue(0, layer.fTranslation:GetValue(layerFrame) + returnVec)
		moho:NewKeyframe(CHANNEL_LAYER_T)
	end
	moho.document:DepthSort()
end

function MR_TransformRigTool:FindSmartbones(moho, skel, targetSkel, mainBone, ignoreGroup)
	local actionsList = {}
	local count = 0
	for a in pairs(self.targetSkelBonesNamesList) do
		self.targetSkelBonesNamesList[a] = nil
	end
	
	for i = 0, targetSkel:CountBones()-1 do
		local bone = targetSkel:Bone(i)
		table.insert(self.targetSkelBonesNamesList, bone:Name())
	end
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		count = count + 1
		if layer then
			local isOk = true
			if ignoreGroup then
				if moho:LayerAsGroup(ignoreGroup):IsLayerValid(layer) or layer == ignoreGroup then
					isOk = false
				end
			end
			if (layer:IsAncestorSelected() or layer:SecondarySelection()) and isOk then
				for a=0, layer:CountActions()-1 do
					local actionName = layer:ActionName(a)
					if layer:ActionDuration(actionName) > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end
				end	
			end
		end
	until not layer
	
	for i = 0, skel:CountBones()-1 do
		local bone = skel:Bone(i)
		if bone.fSelected then
			for actionID = 0, bone.fAnimPos:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
				local actionName = bone.fAnimPos:ActionName(actionID)
				if actionChannel ~= nil then
					if actionChannel:Duration() > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end	
				end			
			end
			for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
				local actionName = bone.fAnimAngle:ActionName(actionID)
				if actionChannel ~= nil then
					if actionChannel:Duration() > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end	
				end			
			end
			for actionID = 0, bone.fAnimScale:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimVal(bone.fAnimScale:Action(actionID))
				local actionName = bone.fAnimScale:ActionName(actionID)
				if actionChannel ~= nil then
					if actionChannel:Duration() > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end	
				end			
			end
			for actionID = 0, bone.fFlipH:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimBool(bone.fFlipH:Action(actionID))
				local actionName = bone.fFlipH:ActionName(actionID)
				if actionChannel ~= nil then
					if actionChannel:Duration() > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end	
				end			
			end
			for actionID = 0, bone.fFlipV:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimBool(bone.fFlipV:Action(actionID))
				local actionName = bone.fFlipV:ActionName(actionID)
				if actionChannel ~= nil then
					if actionChannel:Duration() > 0 then
						local isActionNew = true
						for u, action in pairs(actionsList) do
							if action == actionName then
								isActionNew = false
							end
						end
						if isActionNew then
							table.insert(actionsList, actionName)
						end
					end	
				end			
			end
		end
	end
	for a in pairs(self.smartbonesList.originalName) do
		self.smartbonesList[a] = nil
		self.smartbonesList.originalName[a] = nil
		self.smartbonesList.newName[a] = nil
		self.smartbonesList.mode[a] = nil
	end
	for a in pairs(MR_ActionsDialog.actionInput) do
		MR_ActionsDialog.dynamicNumberText[a] = nil
		MR_ActionsDialog.dynamicSymbolText[a] = nil
		MR_ActionsDialog.actionInput[a] = nil
		MR_ActionsDialog.asActionRadioButton[a] = nil
		MR_ActionsDialog.asSmartboneRadioButton[a] = nil
		MR_ActionsDialog.removeRadioButton[a] = nil
	end
	for u, action in pairs(actionsList) do
		for i = 0, skel:CountBones()-1 do
			local bone = skel:Bone(i)
			if action == bone:Name() and not skel:IsAncestorSelected(i) and not bone.fSelected then
				table.insert(self.smartbonesList.originalName, action)
				break
			end
		end	
	end
	if #self.smartbonesList.originalName >= 1 then
		local dlog = MR_ActionsDialog:new(moho)
		if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
			return false
		end
	end	
	return true
end

function MR_TransformRigTool:DuplicateBodypart(moho, alt)
	local mainSkel = self:FindSkeleton(moho)
	self:FindSkeletonLayer(moho)
	local skelLayer = self.skelLayer
	if not mainSkel or not skelLayer then
		return
	end
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local layer = moho.document:GetSelectedLayer(l)
		if layer == skelLayer then
			local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate bodypart alert 1'),
			self:Localize('Duplicate bodypart alert 2'), "", 'OK')
			return
		end
	end
	
	local isAnotherSkelSelected = false
	local extraSkelCounter = 0
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local layer = moho.document:GetSelectedLayer(l)
		if layer == skelLayer then
			local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate bodypart alert 1'),
			self:Localize('Duplicate bodypart alert 2'), "", 'OK')
			return
		elseif layer:IsBoneType() and not moho:LayerAsGroup(skelLayer):IsLayerValid(layer) then
			isAnotherSkelSelected = true
			extraSkelCounter = extraSkelCounter + 1
		end
	end

	if extraSkelCounter > 0 then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate bodypart alert 1'),
		self:Localize('Duplicate bodypart alert 2'), "", 'OK')
		return
	end
	
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, mainSkel)
	if self.mainSkelSelectedBones ~= 1 then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate bodypart alert 1'),
		self:Localize('Duplicate bodypart alert 2'), "", 'OK')
		return
	end
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local isDirectionRight = true
	local direction1
	local direction2
	local needToRename = true
	
	local dlog = MR_DuplicateBodypartDialog:new(moho)
	if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
		return false
	end
	
	if self.directionMode then
		if self.rDirection then
			isDirectionRight = true
			direction1 = 'R'
			direction2 = 'L'
		else
			isDirectionRight = false
			direction1 = 'L'
			direction2 = 'R'
		end
	elseif	self.customMode then
		direction1 = self.customModeInput1
		direction2 = self.customModeInput2
	elseif	self.suffixMode then
		needToRename = false
	end
	
	moho.document:SetDirty()
	moho.document:PrepUndo(nil)
	local currentLayer = moho.layer
	
	local duplicatedLayersList = {}
	duplicatedLayersList.originalLayers = {}
	duplicatedLayersList.duplicatedLayers = {}
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local originalLayer = moho.document:GetSelectedLayer(l)
		if not originalLayer:IsAncestorSelected() then
			table.insert(duplicatedLayersList.originalLayers, originalLayer)
		end	
	end
	local selectedBoneId = mainSkel:SelectedBoneID()
	local selectedBone = mainSkel:Bone(selectedBoneId)
	
	local originalVitruvianGroup = nil
	local mainBones = {}
	
	if self.transformTargetBones then
		self:FindTargetBones(moho, selectedBone, true)
	end
	if self.isVitruvianBonesAvaible then
		if mainSkel:IsBoneInAGroup(selectedBoneId) and self.transformVitruvianBones then
			originalVitruvianGroup = mainSkel:GroupForBone(selectedBoneId, false)
			for i=0, originalVitruvianGroup:CountBones()-1 do
				local bone = originalVitruvianGroup:Bone(i)
				bone.fSelected = true
				table.insert(mainBones, bone)
				if self.transformTargetBones then
					self:FindTargetBones(moho, bone, false)
				end
			end
		else
			table.insert(mainBones, selectedBone)	
		end
	end
	
	if self.transformTargetBones then
		for n, targetBone in ipairs(self.targetBonesList) do
			targetBone.fSelected = true
		end
	end	
	
	for i=0, mainSkel:CountBones()-1 do
		local bone = mainSkel:Bone(i)
		if mainSkel:IsAncestorSelected(i) then
			bone.fSelected = true
		end
	end
	
	if alt then
		self.duplicateActionsMode = 1
		if not self:FindSmartbones(moho, mainSkel, mainSkel, selectedBone, nil) then
			mainSkel:SelectNone()
			selectedBone.fSelected = true
			return
		end
		
		for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
			if MR_TransformRigTool.smartbonesList.mode[i] == 2 then
				local bone = mainSkel:BoneByName(MR_TransformRigTool.smartbonesList.originalName[i])
				bone.fSelected = true
			end
		end
	end	
	
	moho:SetSelLayer(skelLayer)
	
	local originalTotalBonesNum = mainSkel:CountBones()
	
	local oiginalBonesList = {}
	local duplicatedBonesListID = {}
	
	for i=0, mainSkel:CountBones()-1 do
		local bone = mainSkel:Bone(i)
		if bone.fSelected then
			table.insert(oiginalBonesList, bone)
		end
	end
	
	local isMainBonePin = false
	if selectedBone.fLength == 0 then
		isMainBonePin = true
		selectedBone.fLength = 0.1
	end
	
	moho:Copy()
	
	if isMainBonePin then
		selectedBone.fLength = 0
	end
	
	moho:SetSelLayer(topLayer)
	
	local tempSkelLayer = moho:CreateNewLayer(MOHO.LT_BONE, false)
	local tempSkel = moho:LayerAsBone(tempSkelLayer):Skeleton()
	tempSkelLayer:SetName('Temp Layer')
	
	moho:Paste()
	
	for o, origBone in ipairs(oiginalBonesList) do
		for i =0, tempSkel:CountBones()-1 do
			local bone = tempSkel:Bone(i)
			if isMainBonePin then
				if selectedBone:Name() == bone:Name() then
					bone.fLength = 0
				end
			end
			if origBone:Name() == bone:Name() then
				table.insert(duplicatedBonesListID, i)
				break
			end
		end
	end

	local allOriginalLayersList = {}
	local allDuplicatedLayersList = {}
	local duplicatSufix = self.duplicatedBodypartSuffix
	
	for k, origLayer in ipairs(duplicatedLayersList.originalLayers) do
		if origLayer:IsGroupType() then
			local tempList = self:ScanLayersReccursion(moho, origLayer)
			for t, templayer in ipairs(tempList) do
				table.insert(allOriginalLayersList, templayer)
			end
		else
			table.insert(allOriginalLayersList, origLayer)
		end
		local dupLayer = moho.document:DuplicateLayer(origLayer)
		if dupLayer:IsGroupType() then
			local tempList = self:ScanLayersReccursion(moho, dupLayer)
			for t, templayer in ipairs(tempList) do
				table.insert(allDuplicatedLayersList, templayer)
			end
		else
			table.insert(allDuplicatedLayersList, dupLayer)
		end
		local newDuplicatedLayerName = origLayer:Name()
		if needToRename then
			if self.swapNames then
				local tempText1 = 'TEMPTEXT1'
				local tempText2 = 'TEMPTEXT2'
				newDuplicatedLayerName = self:ChangeNameString(moho, origLayer:Name(), direction1, tempText1)
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, direction2, tempText2)
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, string.lower(direction1), string.lower(tempText1))
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, string.lower(direction2), string.lower(tempText2))
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, tempText1, direction2)
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, tempText2, direction1)
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, string.lower(tempText1), string.lower(direction2))
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, string.lower(tempText2), string.lower(direction1))
			else
				newDuplicatedLayerName = self:ChangeNameString(moho, origLayer:Name(), direction1, direction2)
				newDuplicatedLayerName = self:ChangeNameString(moho, newDuplicatedLayerName, string.lower(direction1), string.lower(direction2))
			end	
		end
		local isNameOk = true
		local targetName = newDuplicatedLayerName
		local counter = ''
		local sufix = ''
		repeat
			isNameOk = true
			for i=0, skelLayer:CountLayers()-1 do
				local layer = skelLayer:Layer(i)
				if targetName..sufix..counter == layer:Name() then
					isNameOk = false
					if counter == '' and sufix ~= '' then
						counter = 0
					end
					if sufix == '' then
						sufix = duplicatSufix
					elseif counter >= 0 then
						sufix = duplicatSufix..self.counterDelimiter
					end
					if counter ~= '' then
						counter = counter + 1
					end	
					break
				end
			end
			newDuplicatedLayerName = targetName..sufix..counter
		until isNameOk
		dupLayer:SetName(newDuplicatedLayerName)
		moho:SetSelLayer(dupLayer)
		if needToRename then
			if dupLayer:IsGroupType() then
				local count = 0
				repeat
					local subLayer = moho.document:LayerByAbsoluteID(count)
					if subLayer then
						count = count + 1
						if moho:LayerAsGroup(dupLayer):IsLayerValid(subLayer) then
							if self.swapNames then
								local tempText1 = 'TEMPTEXT1'
								local tempText2 = 'TEMPTEXT2'
								local newName = subLayer:Name()
								newName = self:ChangeNameString(moho, newName, direction1, tempText1)
								newName = self:ChangeNameString(moho, newName, direction2, tempText2)
								newName = self:ChangeNameString(moho, newName, string.lower(direction1), string.lower(tempText1))
								newName = self:ChangeNameString(moho, newName, string.lower(direction2), string.lower(tempText2))
								newName = self:ChangeNameString(moho, newName, tempText1, direction2)
								newName = self:ChangeNameString(moho, newName, tempText2, direction1)
								newName = self:ChangeNameString(moho, newName, string.lower(tempText1), string.lower(direction2))
								newName = self:ChangeNameString(moho, newName, string.lower(tempText2), string.lower(direction1))
								subLayer:SetName(newName)
							else
								subLayer:SetName(self:ChangeNameString(moho, subLayer:Name(), direction1, direction2))
								subLayer:SetName(self:ChangeNameString(moho, subLayer:Name(), string.lower(direction1), string.lower(direction2)))
							end
						end
					end
				until not subLayer
			end	
		end
		table.insert(duplicatedLayersList.duplicatedLayers, dupLayer)
		moho:PlaceLayerInGroup(dupLayer, moho:LayerAsGroup(tempSkelLayer), true, false)
	end
	
	moho:SetSelLayer(tempSkelLayer)
	
	local duplicatedBonesNamesList = {}
	duplicatedBonesNamesList.original = {}
	duplicatedBonesNamesList.new = {}
	
	for i = 0, tempSkel:CountBones()-1 do
		local bone = tempSkel:Bone(i)
		local isBoneInSBList = false
		local newNameFromList = bone:Name()
		if alt then
			for p = 1, #self.smartbonesList.originalName do
				if bone:Name() == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 2 then
					isBoneInSBList = true
					newNameFromList = self.smartbonesList.newName[p]
				end
			end
		end	
		table.insert(duplicatedBonesNamesList.original, bone:Name())
		
		local newBoneName = bone:Name()
		if isBoneInSBList and alt then
			newBoneName = newNameFromList
		elseif needToRename then
			if self.swapNames then
				local tempText1 = 'TEMPTEXT1'
				local tempText2 = 'TEMPTEXT2'
				newBoneName = self:ChangeNameString(moho, bone:Name(), direction1, tempText1)
				newBoneName = self:ChangeNameString(moho, newBoneName, direction2, tempText2)
				newBoneName = self:ChangeNameString(moho, newBoneName, tempText1, direction2)
				newBoneName = self:ChangeNameString(moho, newBoneName, tempText2, direction1)
				newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(direction1), string.lower(tempText1))
				newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(direction2), string.lower(tempText2))
				newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(tempText1), string.lower(direction2))
				newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(tempText2), string.lower(direction1))
			else
				newBoneName = self:ChangeNameString(moho, bone:Name(), direction1, direction2)
				newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(direction1))
			end
		end	
		local targetName = newBoneName
		local counter = ''
		local isNameOk = true
		local sufix = ''
		repeat
			isNameOk = true
			for i = 0, mainSkel:CountBones()-1 do
				local mainSkelBone = mainSkel:Bone(i)
				if targetName..sufix..counter == mainSkelBone:Name() then
					isNameOk = false
					if counter == '' and sufix ~= '' then
						counter = 0
					end
					if sufix == '' then
						sufix = duplicatSufix
					elseif counter >= 0 then
						sufix = duplicatSufix..self.counterDelimiter
					end
					if counter ~= '' then
						counter = counter + 1
					end	
					break
				end
			end
			newBoneName = targetName..sufix..counter
		until isNameOk
		bone:SetName(newBoneName)
		table.insert(duplicatedBonesNamesList.new, newBoneName)
	end
	
	if self.swapNames then
		self:CleanUpActions(moho, tempSkelLayer, false, isDirectionRight, needToRename, duplicatedBonesNamesList.original)
	else
		self:CleanUpActions(moho, tempSkelLayer, true, isDirectionRight, needToRename, duplicatedBonesNamesList.original)
	end
	local actionNames = {}
	
	for k, layer in ipairs(allDuplicatedLayersList) do
		if needToRename then
			if layer:IsBoneType() then
				local layerSkel = moho:LayerAsBone(layer):Skeleton()
				if layerSkel ~= tempSkel then
					for i =0, layerSkel:CountBones()-1 do
						local bone = layerSkel:Bone(i)
						if self.swapNames then
							local tempText1 = 'TEMPTEXT1'
							local tempText2 = 'TEMPTEXT2'
							local newName = bone:Name()
							newName = self:ChangeNameString(moho, newName, direction1, tempText1)
							newName = self:ChangeNameString(moho, newName, direction2, tempText2)
							newName = self:ChangeNameString(moho, newName, tempText1, direction2)
							newName = self:ChangeNameString(moho, newName, tempText2, direction1)
							newName = self:ChangeNameString(moho, newName, string.lower(direction1), string.lower(tempText1))
							newName = self:ChangeNameString(moho, newName, string.lower(direction2), string.lower(tempText2))
							newName = self:ChangeNameString(moho, newName, string.lower(tempText1), string.lower(direction2))
							newName = self:ChangeNameString(moho, newName, string.lower(tempText2), string.lower(direction1))
							bone:SetName(newName)
						else
							bone:SetName(self:ChangeNameString(moho, bone:Name(), direction1, direction2))
							bone:SetName(self:ChangeNameString(moho, bone:Name(), string.lower(direction1), string.lower(direction2)))
						end	
					end
				end	
			end
		end	
		for n, orLayer in ipairs(allOriginalLayersList) do
			if layer:GetDistortionMeshLayer() == orLayer then
				layer:SetDistortionMeshLayer(allDuplicatedLayersList[n])
			end
		end	
		
		local layerActionNames = {}
		for i=0, layer:CountActions()-1 do
			local actionName = layer:ActionName(i)
			table.insert(layerActionNames, actionName)
		end
		table.insert(actionNames, layerActionNames)
	end
	if alt then
		for p = 1, #self.smartbonesList.originalName do
			tempSkelLayer:ActivateAction(self.smartbonesList.originalName[p])
		end	
		tempSkelLayer:ActivateAction(nil)
	end	
	
	local actionsToRemoveList = {}
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		local isActionHasBone = false
		local newBoneName = ''
		local newName = actionName
		for o, boneName in pairs(duplicatedBonesNamesList.original) do
			if actionName == boneName then
				isActionHasBone = true
				newBoneName = duplicatedBonesNamesList.new[o]
				break
			elseif actionName == boneName..' 2' and skelLayer:IsSmartBoneAction(actionName) then
				local isActionDouble = false
				for i = 0, mainSkel:CountBones()-1 do
					local boneName = mainSkel:Bone(i):Name()
					if boneName == actionName then
						isActionDouble = false
					end
				end
				if not isActionDouble then
					isActionHasBone = true
					newBoneName = duplicatedBonesNamesList.new[o]..' 2'
					break
				end	
			end
		end
		local isActionNeedToRemove = false
		if alt then
			for p = 1, #self.smartbonesList.originalName do
				if actionName == self.smartbonesList.originalName[p]..' 2' and self.smartbonesList.mode[p] == 1 then
					newName = self.smartbonesList.newName[p]
				elseif actionName == self.smartbonesList.originalName[p]..' 2' and self.smartbonesList.mode[p] == 3 then
					isActionNeedToRemove = true
				end
			end
			for p = 1, #self.smartbonesList.originalName do
				if actionName == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 1 then
					newName = self.smartbonesList.newName[p]
				elseif actionName == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 3 then
					isActionNeedToRemove = true
				end
			end
		end	
		if not isActionNeedToRemove then
			if isActionHasBone then
				newName = newBoneName
			end
			if newName ~= actionName then
				tempSkelLayer:RenameAction(actionName, newName)
			end
		else
			local isActionNew = true
			for u, action in pairs(actionsToRemoveList) do
				if action == actionName then
					isActionNew = false
				end
			end
			if isActionNew then
				table.insert(actionsToRemoveList, actionName)
			end	
		end	
	end
	if alt then
		if #actionsToRemoveList > 0 then 
			local count = 0
			repeat
				local layer = moho.document:LayerByAbsoluteID(count)
				count = count + 1
				if layer then
					if tempSkelLayer:IsLayerValid(layer) or layer == tempSkelLayer then
						for _,name in ipairs(actionsToRemoveList) do
							layer:DeleteAction(name)
						end
					end
				end
			until not layer	
		end
	end
	
	for k = 1, #allDuplicatedLayersList do
		for i = 1, #actionNames[k] do
			local actionName = actionNames[k][i]
			local isActionHasBone = false
			local newBoneName = ''
			local newName = actionName
			for o, boneName in pairs(duplicatedBonesNamesList.original) do
				if actionName == boneName then
					isActionHasBone = true
					newBoneName = duplicatedBonesNamesList.new[o]
					break
				end
			end
			if isActionHasBone then
				newName = newBoneName
			else
				if needToRename then
					if self.swapNames then
						local tempText1 = 'TEMPTEXT1'
						local tempText2 = 'TEMPTEXT2'
						newName = self:ChangeNameString(moho, actionName, direction1, tempText1)
						newName = self:ChangeNameString(moho, newName, direction2, tempText2)
						newName = self:ChangeNameString(moho, newName, string.lower(direction1), string.lower(tempText1))
						newName = self:ChangeNameString(moho, newName, string.lower(direction2), string.lower(tempText2))
						newName = self:ChangeNameString(moho, newName, tempText1, direction2)
						newName = self:ChangeNameString(moho, newName, tempText2, direction1)
						newName = self:ChangeNameString(moho, newName, string.lower(tempText1), string.lower(direction2))
						newName = self:ChangeNameString(moho, newName, string.lower(tempText2), string.lower(direction1))
					else
						newName = self:ChangeNameString(moho, actionName, direction1, direction2)
						newName = self:ChangeNameString(moho, newName, string.lower(direction1), string.lower(direction2))
					end	
				end	
			end	
			if newName ~= actionName then
				allDuplicatedLayersList[k]:RenameAction(actionName, newName)
			end
		end
	end
	
	if originalVitruvianGroup and needToRename then
		for g=0, tempSkel:CountGroups()-1 do
			local group = tempSkel:Group(g)
			local newName 
			if self.swapNames then
				local tempText1 = 'TEMPTEXT1'
				local tempText2 = 'TEMPTEXT2'
				newName = self:ChangeNameString(moho, group:Name(), direction1, tempText1)
				newName = self:ChangeNameString(moho, newName, direction2, tempText2)
				newName = self:ChangeNameString(moho, newName, string.lower(direction1), string.lower(tempText1))
				newName = self:ChangeNameString(moho, newName, string.lower(direction2), string.lower(tempText2))
				newName = self:ChangeNameString(moho, newName, tempText1, direction2)
				newName = self:ChangeNameString(moho, newName, tempText2, direction1)
				newName = self:ChangeNameString(moho, newName, string.lower(tempText1), string.lower(direction2))
				newName = self:ChangeNameString(moho, newName, string.lower(tempText2), string.lower(direction1))
			else
				newName = self:ChangeNameString(moho, group:Name(), direction1, direction2)
			end	
			if group:Name() ~= newName then
				group:SetName(newName)
			end
		end	
	end

	tempSkel:SelectAll()
	moho:Copy()
	moho:SetSelLayer(skelLayer)
	mainSkel:SelectNone()
	moho:Paste()

	local allDuplicatedLayers = {}
	for k, dupLayer in ipairs(duplicatedLayersList.duplicatedLayers) do
		moho:PlaceLayerInGroup(dupLayer, moho:LayerAsGroup(skelLayer), true, false)
		if dupLayer:IsGroupType() then
			local count = 0
			repeat
				local layer = moho.document:LayerByAbsoluteID(count)
				count = count + 1
				if layer then
					if moho:LayerAsGroup(dupLayer):IsLayerValid(layer) then
						table.insert(allDuplicatedLayers, layer)
					elseif layer == dupLayer then
						table.insert(allDuplicatedLayers, layer)
					end
				end
			until not layer
		else
			table.insert(allDuplicatedLayers, dupLayer)
		end	
	end
	for k, dupLayer in ipairs(allDuplicatedLayers) do
		if dupLayer:ControllingSkeleton() == mainSkel then
			if moho:LayerAsVector(dupLayer) then
				local mesh = moho:LayerAsVector(dupLayer):Mesh()
				for i=0, mesh:CountPoints()-1 do
					local point = mesh:Point(i)
					for b = 1, #oiginalBonesList do
						local bone = oiginalBonesList[b]
						if point.fParent >= 0 and point.fParent == mainSkel:BoneID(bone) then
							local newBoneID = duplicatedBonesListID[b] + originalTotalBonesNum
							point.fParent = newBoneID
							break
						end
					end
				end	
			end
			local bonesInFlexiBoneSubset = {}
			for u = 1, #oiginalBonesList do
				local bone = oiginalBonesList[u]
				if dupLayer:IsIncludedInFlexiBoneSubset(mainSkel:BoneID(bone)) then
					local newBoneID = duplicatedBonesListID[u] + originalTotalBonesNum
					table.insert(bonesInFlexiBoneSubset, newBoneID)
					-- break
				end
			end
			for u = 1, #oiginalBonesList do
				local bone = oiginalBonesList[u]
				if dupLayer:LayerParentBone() == mainSkel:BoneID(bone) then
					local newBoneID = duplicatedBonesListID[u] + originalTotalBonesNum
					dupLayer:SetLayerParentBone(newBoneID)
					break
				end
			end	
			dupLayer:ClearFlexiBoneSubset()
			for s, boneId in ipairs(bonesInFlexiBoneSubset) do
				dupLayer:AddToFlexiBoneSubset(boneId)
			end
		end
	end
	
	for b = 1, #oiginalBonesList do
		local bone = oiginalBonesList[b]
		for k, mainBone in ipairs(mainBones) do
			if mainBone == bone then
				local duplicatedMainBone = mainSkel:Bone(duplicatedBonesListID[b] + originalTotalBonesNum)
				duplicatedMainBone.fParent = mainBone.fParent
				duplicatedMainBone.fAnimParent:SetValue(0, mainBone.fParent)
				duplicatedMainBone.fAnimPos:SetValue(0, mainBone.fAnimPos:GetValue(0))
				duplicatedMainBone.fAnimAngle:SetValue(0, mainBone.fAnimAngle:GetValue(0))
			end
		end	
		if selectedBone == bone then
			local duplicatedMainBone = mainSkel:Bone(duplicatedBonesListID[b] + originalTotalBonesNum)
			
			mainSkel:SelectNone()
			duplicatedMainBone.fSelected = true
		end
	end	
	
	if self.transformTargetBones then
		for i = originalTotalBonesNum, mainSkel:CountBones() - 1 do
			local bone = mainSkel:Bone(i)
			for actionID = 0, bone.fTargetBone:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimVal(bone.fTargetBone:Action(actionID))
				if actionChannel ~= nil then
					for keyID = 0, actionChannel:CountKeys() - 1 do
						local actionFrame = actionChannel:GetKeyWhen(keyID)
						if (actionFrame > 0) then
							local targetId = actionChannel:GetValue(actionFrame)
							for t = 1, #oiginalBonesList do
								local targetBoneCandidat = oiginalBonesList[t]
								if mainSkel:Bone(targetId) == targetBoneCandidat then
									local newTargetBoneID = duplicatedBonesListID[t] + originalTotalBonesNum
									actionChannel:SetValue(actionFrame, newTargetBoneID)
									local pastedTargetBone = mainSkel:Bone(newTargetBoneID)
									break
								end
							end
						end
					end
				end
			end	
		end
	end
	
	for d=0, tempSkel:CountBones()-1 do
		tempSkel:DeleteBone(0)
	end
	local actionsToRemove = {}
	
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		table.insert(actionsToRemove, actionName)
	end
	
	for l, action in ipairs(actionsToRemove) do
		tempSkelLayer:DeleteAction(action)
	end
	
	moho:DeleteLayer(tempSkelLayer)
	moho:SetSelLayer(duplicatedLayersList.duplicatedLayers[1])
	for i, layer in ipairs(duplicatedLayersList.duplicatedLayers) do
		layer:SetSecondarySelection(true)
	end

	moho:SetCurFrame(1)
	moho:SetCurFrame(0)
	moho.view:DrawMe()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
end

function MR_TransformRigTool:CopyBodypart(moho, alt)
	local mainSkel = self:FindSkeleton(moho)
	self:FindSkeletonLayer(moho)
	local skelLayer = self.skelLayer
	if not mainSkel or not skelLayer then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Copy bodypart alert 1'),
		self:Localize('Copy bodypart alert 2'), "", 'OK')
		return
	end
	
	local isAnotherSkelSelected = false
	local skelLayerToCopy
	local extraSkelCounter = 0
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local layer = moho.document:GetSelectedLayer(l)
		if layer == skelLayer then
			local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Copy bodypart alert 1'),
			self:Localize('Copy bodypart alert 2'), "", 'OK')
			return
		elseif layer:IsBoneType() and not moho:LayerAsGroup(skelLayer):IsLayerValid(layer) then
			isAnotherSkelSelected = true
			skelLayerToCopy = layer
			extraSkelCounter = extraSkelCounter + 1
		end
	end
	
	if skelLayerToCopy == nil or extraSkelCounter ~= 1 then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Copy bodypart alert 1'),
		self:Localize('Copy bodypart alert 2'), "", 'OK')
		return
	end
	
	local skelToCopy = moho:LayerAsBone(skelLayerToCopy):Skeleton()
	
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, mainSkel)
	
	if self.mainSkelSelectedBones ~= 1 or not isAnotherSkelSelected then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Copy bodypart alert 1'),
		self:Localize('Copy bodypart alert 2'), "", 'OK')
		return
	end
	
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	
	moho.document:SetDirty()
	moho.document:PrepUndo()
	local currentLayer = moho.layer
	
	local duplicatedLayersList = {}
	duplicatedLayersList.originalLayers = {}
	duplicatedLayersList.duplicatedLayers = {}
	for l = 0, moho.document:CountSelectedLayers()-1 do
		local originalLayer = moho.document:GetSelectedLayer(l)
		if originalLayer ~= skelLayerToCopy and not originalLayer:IsAncestorSelected() then
			table.insert(duplicatedLayersList.originalLayers, originalLayer)
		end
	end
	local selectedBoneId = mainSkel:SelectedBoneID()
	local selectedBone = mainSkel:Bone(selectedBoneId)
	local originalVitruvianGroup = nil
	local mainBones = {}
	if self.transformTargetBones then
		self:FindTargetBones(moho, selectedBone, true)
	end
	selectedBone.fTempPos = selectedBone.fAnimPos:GetValue(0)
	selectedBone.fTempAngle = selectedBone.fAnimAngle:GetValue(0)
	if self.isVitruvianBonesAvaible then
		if mainSkel:IsBoneInAGroup(selectedBoneId) and self.transformVitruvianBones then
			originalVitruvianGroup = mainSkel:GroupForBone(selectedBoneId, false)
			for i=0, originalVitruvianGroup:CountBones()-1 do
				local bone = originalVitruvianGroup:Bone(i)
				bone.fSelected = true
				table.insert(mainBones, bone)
				if self.transformTargetBones then
					self:FindTargetBones(moho, bone, false)
				end
			end
		else
			table.insert(mainBones, selectedBone)	
		end
	end
	
	if self.transformTargetBones then
		for n, targetBone in ipairs(self.targetBonesList) do
			targetBone.fSelected = true
		end
	end	
	
	local hiddenBonesList = {}
	
	for i=0, mainSkel:CountBones()-1 do
		local bone = mainSkel:Bone(i)
		if mainSkel:IsAncestorSelected(i) then
			if bone.fHidden then
				bone.fHidden = false
				table.insert(hiddenBonesList, bone)
			end
			bone.fSelected = true
		end
	end
	
	if alt then
		self.duplicateActionsMode = 2
		if not self:FindSmartbones(moho, mainSkel, skelToCopy, selectedBone, skelLayerToCopy) then
			mainSkel:SelectNone()
			selectedBone.fSelected = true
			return
		end
		
		for i = 1, #MR_TransformRigTool.smartbonesList.originalName do
			if MR_TransformRigTool.smartbonesList.mode[i] == 2 then
				local bone = mainSkel:BoneByName(MR_TransformRigTool.smartbonesList.originalName[i])
				bone.fSelected = true
			end
		end
	end
	
	moho:SetSelLayer(skelLayer)
	
	local originalTotalBonesNum = skelToCopy:CountBones()
	
	local oiginalBonesList = {}
	local duplicatedBonesListID = {}
	
	for i=0, mainSkel:CountBones()-1 do
		local bone = mainSkel:Bone(i)
		if bone.fSelected then
			table.insert(oiginalBonesList, bone)
		end
	end
	
	local isMainBonePin = false
	if selectedBone.fLength == 0 then
		isMainBonePin = true
		selectedBone.fLength = 0.1
	end
	
	moho:Copy()
	
	if isMainBonePin then
		selectedBone.fLength = 0
	end
	
	moho:SetSelLayer(topLayer)
	
	local tempSkelLayer = moho:CreateNewLayer(MOHO.LT_BONE, false)
	local tempSkel = moho:LayerAsBone(tempSkelLayer):Skeleton()
	tempSkelLayer:SetName('Temp Layer')
	
	moho:Paste()
	
	for o, origBone in ipairs(oiginalBonesList) do
		for i =0, tempSkel:CountBones()-1 do
			local bone = tempSkel:Bone(i)
			if isMainBonePin then
				if selectedBone:Name() == bone:Name() then
					bone.fLength = 0
				end
			end
			if origBone:Name() == bone:Name() then
				table.insert(duplicatedBonesListID, i)
				break
			end
		end
	end

	local allOriginalLayersList = {}
	local allDuplicatedLayersList = {}
	local duplicatSufix = self.duplicatedBodypartSuffix
	
	for k, origLayer in ipairs(duplicatedLayersList.originalLayers) do
		if origLayer:IsGroupType() then
			local tempList = self:ScanLayersReccursion(moho, origLayer)
			for t, templayer in ipairs(tempList) do
				table.insert(allOriginalLayersList, templayer)
			end
		else
			table.insert(allOriginalLayersList, origLayer)
		end
		local dupLayer = moho.document:DuplicateLayer(origLayer)
		if dupLayer:IsGroupType() then
			local tempList = self:ScanLayersReccursion(moho, dupLayer)
			for t, templayer in ipairs(tempList) do
				table.insert(allDuplicatedLayersList, templayer)
			end
		else
			table.insert(allDuplicatedLayersList, dupLayer)
		end

		local newDuplicatedLayerName = origLayer:Name()
		local isNameOk = true
		local targetName = newDuplicatedLayerName
		local counter = ''
		local sufix = ''
		repeat
			isNameOk = true
			for i=0, skelLayerToCopy:CountLayers()-1 do
				local layer = skelLayerToCopy:Layer(i)
				if targetName..sufix..counter == layer:Name() then
					isNameOk = false
					if counter == '' and sufix ~= '' then
						counter = 0
					end
					if sufix == '' then
						sufix = duplicatSufix
					elseif counter >= 0 then
						sufix = duplicatSufix..self.counterDelimiter
					end
					if counter ~= '' then
						counter = counter + 1
					end	
					break
				end
			end
			newDuplicatedLayerName = targetName..sufix..counter
		until isNameOk
		dupLayer:SetName(newDuplicatedLayerName)

		moho:SetSelLayer(dupLayer)
		table.insert(duplicatedLayersList.duplicatedLayers, dupLayer)
		moho:PlaceLayerInGroup(dupLayer, moho:LayerAsGroup(tempSkelLayer), true, false)
	end
	
	moho:SetSelLayer(tempSkelLayer)
	
	for s=0, tempSkel:CountBones()-1 do
		local bone = tempSkel:Bone(s)
		if bone.fParent < 0 then
			local originalBone = mainSkel:BoneByName(bone:Name())
			local deltaAngle = bone.fAnimAngle:GetValue(0) - originalBone.fAnimAngle:GetValue(0)
			for act = 0, tempSkelLayer:CountActions()-1 do
				local actionName = tempSkelLayer:ActionName(act)
				local actionChannelPos = bone.fAnimPos:ActionByName(actionName)
				if actionChannelPos and actionChannelPos:Duration() > 0 then
					for i = 1, actionChannelPos:CountKeys()-1 do
						local keyTime = actionChannelPos:GetKeyWhen(i)
						if keyTime > 0 then
							tempSkelLayer:ActivateAction(actionName)
							moho:SetCurFrame(keyTime)
							local parentOffsetZero = LM.Vector2:new_local()
							local bonePosFrameZero = LM.Vector2:new_local()	
							bonePosFrameZero:Set(bone.fAnimPos:GetValue(0))
							local oldMatrix = LM.Matrix:new_local()
							oldMatrix:Identity()
							local bonePos = LM.Vector2:new_local()	
							bonePos:Set(bone.fAnimPos:GetValue(keyTime))
							local origBoneGlobalPos = LM.Vector2:new_local()
							origBoneGlobalPos:Set(originalBone.fAnimPos:GetValue(0))
							if originalBone.fParent >= 0 then
								local originalParent = mainSkel:Bone(originalBone.fParent)
								oldMatrix:Set(originalParent.fRestMatrix)
								originalParent.fRestMatrix:Transform(origBoneGlobalPos)
								oldMatrix:Transform(bonePos)
							end
							local bonePosDifFrameZero = LM.Vector2:new_local()
							bonePosDifFrameZero = origBoneGlobalPos - bonePosFrameZero
							local localBonePosFrameZero = LM.Vector2:new_local()	
							localBonePosFrameZero:Set(bone.fAnimPos:GetValue(0))
							bonePos = bonePos - bonePosDifFrameZero
							bone.fAnimPos:SetValue(keyTime, bonePos)
							tempSkelLayer:ActivateAction(nil)
						end
					end
				end
			end	
			for act = 0, bone.fAnimAngle:CountActions()-1 do
				local actionChannelAngle = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(act))
				if actionChannelAngle and actionChannelAngle:Duration() > 0 then
					for i=1, actionChannelAngle:CountKeys()-1 do
						local keyTime = actionChannelAngle:GetKeyWhen(i)
						if keyTime > 0 then
							actionChannelAngle:SetValue(keyTime, actionChannelAngle:GetValue(keyTime) + deltaAngle)
						end
					end
				end
			end	
		end
	end
	
	self:CleanUpActions(moho, tempSkelLayer, false, false, true)
	local actionNames = {}
	
	for k, layer in ipairs(allDuplicatedLayersList) do
		for n, orLayer in ipairs(allOriginalLayersList) do
			if layer:GetDistortionMeshLayer() == orLayer then
				layer:SetDistortionMeshLayer(allDuplicatedLayersList[n])
			end
		end	
		
		local layerActionNames = {}
		for i=0, layer:CountActions()-1 do
			local actionName = layer:ActionName(i)
			table.insert(layerActionNames, actionName)
		end
		table.insert(actionNames, layerActionNames)
	end
	
	for b, bone in ipairs(hiddenBonesList) do
		bone.fHidden = true
		tempSkel:BoneByName(bone:Name()).fHidden = true
	end
	
	local duplicatedBonesNamesList = {}
	duplicatedBonesNamesList.original = {}
	duplicatedBonesNamesList.new = {}
	
	for s=0, tempSkel:CountBones()-1 do
		local bone = tempSkel:Bone(s)
		local isBoneInSBList = false
		local newNameFromList = bone:Name()
		if alt then
			for p = 1, #self.smartbonesList.originalName do
				if bone:Name() == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 2 then
					isBoneInSBList = true
					newNameFromList = self.smartbonesList.newName[p]
				end
			end
		end	
		table.insert(duplicatedBonesNamesList.original, bone:Name())
		local newBoneName = bone:Name()
		if isBoneInSBList and alt then
			newBoneName = newNameFromList
		end
		local targetName = newBoneName
		local counter = ''
		local isNameOk = true
		local sufix = ''
		repeat
			isNameOk = true
			for i = 0, skelToCopy:CountBones()-1 do
				local mainSkelBone = skelToCopy:Bone(i)
				if targetName..sufix..counter == mainSkelBone:Name() then
					isNameOk = false
					if counter == '' and sufix ~= '' then
						counter = 0
					end
					if sufix == '' then
						sufix = duplicatSufix..' '
					end
					if counter ~= '' then
						counter = counter + 1
					end	
					break
				end
			end
			newBoneName = targetName..sufix..counter
		until isNameOk
		bone:SetName(newBoneName)
		table.insert(duplicatedBonesNamesList.new, newBoneName)
	end
	
	if alt then
		for p = 1, #self.smartbonesList.originalName do
			tempSkelLayer:ActivateAction(self.smartbonesList.originalName[p])
		end	
		tempSkelLayer:ActivateAction(nil)
	end	
	
	local actionsToRemoveList = {}
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		local isActionHasBone = false
		local newBoneName = ''
		local newName = actionName
		for o, boneName in pairs(duplicatedBonesNamesList.original) do
			if actionName == boneName then
				isActionHasBone = true
				newBoneName = duplicatedBonesNamesList.new[o]
				break
			elseif actionName == boneName..' 2' and skelLayer:IsSmartBoneAction(actionName) then
				local isActionDouble = false
				for i = 0, mainSkel:CountBones()-1 do
					local boneName = mainSkel:Bone(i):Name()
					if boneName == actionName then
						isActionDouble = true
					end
				end
				if not isActionDouble then
					isActionHasBone = true
					newBoneName = duplicatedBonesNamesList.new[o]..' 2'
					break
				end	
			end
		end
		local isActionNeedToRemove = false
		if alt then
			for p = 1, #self.smartbonesList.originalName do
				if actionName == self.smartbonesList.originalName[p]..' 2' and self.smartbonesList.mode[p] == 1 then
					newName = self.smartbonesList.newName[p]..' 2'
				elseif actionName == self.smartbonesList.originalName[p]..' 2' and self.smartbonesList.mode[p] == 3 then
					isActionNeedToRemove = true
				end
			end
			for p = 1, #self.smartbonesList.originalName do
				if actionName == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 1 then
					newName = self.smartbonesList.newName[p]
				elseif actionName == self.smartbonesList.originalName[p] and self.smartbonesList.mode[p] == 3 then
					isActionNeedToRemove = true
				end
			end
		end
		
		if not isActionNeedToRemove then
			if isActionHasBone then
				newName = newBoneName
			end
			if newName ~= actionName then
				tempSkelLayer:RenameAction(actionName, newName)
			end
		else
			local isActionNew = true
			for u, action in pairs(actionsToRemoveList) do
				if action == actionName then
					isActionNew = false
				end
			end
			if isActionNew then
				table.insert(actionsToRemoveList, actionName)
			end
		end	
	end
	
	if alt then
		if #actionsToRemoveList > 0 then 
			local count = 0
			repeat
				local layer = moho.document:LayerByAbsoluteID(count)
				count = count + 1
				if layer then
					if tempSkelLayer:IsLayerValid(layer) or layer == tempSkelLayer then
						for _,name in ipairs(actionsToRemoveList) do
							layer:DeleteAction(name)
						end
					end
				end
			until not layer	
		end
	end
	
	for k = 1, #allDuplicatedLayersList do
		for i = 1, #actionNames[k] do
			local actionName = actionNames[k][i]
			local isActionHasBone = false
			local newBoneName = ''
			local newName = actionName
			for o, boneName in pairs(duplicatedBonesNamesList.original) do
				if actionName == boneName then
					isActionHasBone = true
					newBoneName = duplicatedBonesNamesList.new[o]
					break
				end
			end
			if isActionHasBone then
				newName = newBoneName
			end	
			if newName ~= actionName then
				allDuplicatedLayersList[k]:RenameAction(actionName, newName)
			end
		end
	end

	tempSkel:SelectAll()
	moho:Copy()
	moho:SetSelLayer(skelLayerToCopy)
	skelToCopy:SelectNone()
	moho:Paste()

	local allDuplicatedLayers = {}
	for k, dupLayer in ipairs(duplicatedLayersList.duplicatedLayers) do
		moho:PlaceLayerInGroup(dupLayer, moho:LayerAsGroup(skelLayerToCopy), true, false)
		if dupLayer:IsGroupType() then
			local count = 0
			repeat
				local layer = moho.document:LayerByAbsoluteID(count)
				count = count + 1
				if layer then
					if moho:LayerAsGroup(dupLayer):IsLayerValid(layer) then
						table.insert(allDuplicatedLayers, layer)
					elseif layer == dupLayer then
						table.insert(allDuplicatedLayers, layer)
					end
				end
			until not layer
		else
			table.insert(allDuplicatedLayers, dupLayer)
		end	
	end
	for k, dupLayer in ipairs(allDuplicatedLayers) do
		if dupLayer:ControllingSkeleton() == skelToCopy then
			if moho:LayerAsVector(dupLayer) then
				local mesh = moho:LayerAsVector(dupLayer):Mesh()
				for i=0, mesh:CountPoints()-1 do
					local point = mesh:Point(i)
					for b = 1, #oiginalBonesList do
						local bone = oiginalBonesList[b]
						if point.fParent >= 0 and point.fParent == mainSkel:BoneID(bone) then
							local newBoneID = duplicatedBonesListID[b] + originalTotalBonesNum
							point.fParent = newBoneID
							break
						end
					end
				end	
			end
			local bonesInFlexiBoneSubset = {}
			for u = 1, #oiginalBonesList do
				local bone = oiginalBonesList[u]
				if dupLayer:IsIncludedInFlexiBoneSubset(mainSkel:BoneID(bone)) then
					local newBoneID = duplicatedBonesListID[u] + originalTotalBonesNum
					table.insert(bonesInFlexiBoneSubset, newBoneID)
					-- break
				end
			end
			for u = 1, #oiginalBonesList do
				local bone = oiginalBonesList[u]
				if dupLayer:LayerParentBone() == mainSkel:BoneID(bone) then
					local newBoneID = duplicatedBonesListID[u] + originalTotalBonesNum
					dupLayer:SetLayerParentBone(newBoneID)
					break
				end
			end	
			dupLayer:ClearFlexiBoneSubset()
			for s, boneId in ipairs(bonesInFlexiBoneSubset) do
				dupLayer:AddToFlexiBoneSubset(boneId)
			end
		end
	end
	for b = 1, #oiginalBonesList do
		local bone = oiginalBonesList[b]
		if selectedBone == bone then
			local duplicatedMainBone = skelToCopy:Bone(duplicatedBonesListID[b] + originalTotalBonesNum)
			
			skelToCopy:SelectNone()
			duplicatedMainBone.fSelected = true
		end
	end	
	
	if self.transformTargetBones then
		for i = originalTotalBonesNum, skelToCopy:CountBones() - 1 do
			local bone = skelToCopy:Bone(i)
			for actionID = 0, bone.fTargetBone:CountActions() - 1 do
				local actionChannel = moho:ChannelAsAnimVal(bone.fTargetBone:Action(actionID))
				if actionChannel ~= nil then
					for keyID = 0, actionChannel:CountKeys() - 1 do
						local actionFrame = actionChannel:GetKeyWhen(keyID)
						if (actionFrame > 0) then
							local targetId = actionChannel:GetValue(actionFrame)
							for t = 1, #oiginalBonesList do
								local targetBoneCandidat = oiginalBonesList[t]
								if mainSkel:Bone(targetId) == targetBoneCandidat then
									local newTargetBoneID = duplicatedBonesListID[t] + originalTotalBonesNum
									actionChannel:SetValue(actionFrame, newTargetBoneID)
									local pastedTargetBone = skelToCopy:Bone(newTargetBoneID)
									break
								end
							end
						end
					end
				end
			end	
		end
	end
	
	for d=0, tempSkel:CountBones()-1 do
		tempSkel:DeleteBone(0)
	end
	local actionsToRemove = {}
	
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		table.insert(actionsToRemove, actionName)
	end
	
	for l, action in ipairs(actionsToRemove) do
		tempSkelLayer:DeleteAction(action)
	end
	
	mainSkel:SelectNone()
	selectedBone.fSelected = true
	
	moho:DeleteLayer(tempSkelLayer)
	moho:SetSelLayer(duplicatedLayersList.duplicatedLayers[1])
	for i, layer in ipairs(duplicatedLayersList.duplicatedLayers) do
		layer:SetSecondarySelection(true)
	end
	moho:SetCurFrame(1)
	moho:SetCurFrame(0)
	moho.view:DrawMe()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
end

function MR_TransformRigTool:ChangeNameString(moho, name, t1, t2, viceVersa)
	if not name then
		return
	end
	local viceVersa = viceVersa or false
	local text1 = t1 or 'R'
	local text2 = t2 or 'L'
	
	local layerName = name
	if string.gsub(layerName, "% "..text1.." ", " "..text2.." ") ~= layerName then
		layerName = string.gsub(layerName, "% "..text1.." ", " "..text2.." ")
	elseif string.gsub(layerName, "% "..text2.." ", " "..text1.." ") ~= layerName and viceVersa then
		layerName = string.gsub(layerName, "% "..text2.." ", " "..text1.." ")
	end
	
	if string.gsub(layerName, "%_"..text1.."_", "_"..text2.."_") ~= layerName then
		layerName = string.gsub(layerName, "%_"..text1.."_", "_"..text2.."_")
	elseif string.gsub(layerName, "%_"..text2.."_", "_"..text1.."_") ~= layerName and viceVersa then
		layerName = string.gsub(layerName, "%_"..text2.."_", "_"..text1.."_")
	end
	
	if string.gsub(layerName, "% "..text1.."_", " "..text2.."_") ~= layerName then
		layerName = string.gsub(layerName, "% "..text1.."_", " "..text2.."_")
	elseif string.gsub(layerName, "% "..text2.."_", " "..text1.."_") ~= layerName and viceVersa then
		layerName = string.gsub(layerName, "% "..text2.."_", " "..text1.."_")
	end	
	if string.gsub(layerName, "%_"..text1.." ", "_"..text2.." ") ~= layerName then
		layerName = string.gsub(layerName, "%_"..text1.." ", "_"..text2.." ")
	elseif string.gsub(layerName, "%_"..text2.." ", "_"..text1.." ") ~= layerName and viceVersa then
		layerName =string.gsub(layerName, "%_"..text2.." ", "_"..text1.." ")
	end

	if string.sub(layerName, layerName:len() - text1:len()) == " "..text1 then
		layerName = string.sub(layerName, 0, -text1:len()-2).." "..text2
	elseif string.sub(layerName, layerName:len() - text2:len()) == " "..text2 and viceVersa then
		layerName = string.sub(layerName, 0, -text2:len()-2).." "..text1
	end	
	if string.sub(layerName, layerName:len() - text1:len()) == "_"..text1 then
		layerName = string.sub(layerName, 0, -text1:len()-2).."_"..text2
	elseif string.sub(layerName, layerName:len() - text2:len()) == "_"..text2 and viceVersa then
		layerName = string.sub(layerName, 0, -text2:len()-2).."_"..text1
	end
	
	if string.sub(layerName, 0, text1:len()+1) == text1.." " then
		layerName = text2.." "..string.sub(layerName, text1:len() + 2, layerName:len())
	elseif string.sub(layerName, 0, text2:len() + 1) == text2.." " and viceVersa then
		layerName = text1.." "..string.sub(layerName, text2:len() + 2, layerName:len())
	end	
	if string.sub(layerName, 0, text1:len() + 1) == text1.."_" then
		layerName = text2.."_"..string.sub(layerName, text1:len() + 2, layerName:len())
	elseif string.sub(layerName, 0, text2:len() + 1) == text2.."_" and viceVersa then
		layerName = text1.."_"..string.sub(layerName, text2:len() + 2, layerName:len())
	end
	return layerName
end

function MR_TransformRigTool:CleanUpActions(moho, group, clenupDirection, direction, considerDirection, bonesNamesList)
	local count = 0
	local direction1
	local direction2
	if self.directionMode then
		if direction then
			direction1 = 'R'
			direction2 = 'L'
		else
			direction1 = 'L'
			direction2 = 'R'
		end
	elseif	self.customMode then
		direction1 = self.customModeInput1
		direction2 = self.customModeInput2
	elseif	self.suffixMode then
		direction1 = ''
		direction2 = ''
	end
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		count = count + 1
		if layer then
			local actionsToRemove = {}
			if group:IsLayerValid(layer) or layer == group then
				for a=0, layer:CountActions()-1 do
					local actionName = layer:ActionName(a)
					if layer:ActionDuration(actionName) < 1 then
						table.insert(actionsToRemove, actionName)
					elseif self:ChangeNameString(moho, actionName, direction2, direction1) ~= actionName and clenupDirection and considerDirection then
						table.insert(actionsToRemove, actionName)
					elseif self:ChangeNameString(moho, actionName, string.lower(direction2), string.lower(direction1)) ~= actionName and clenupDirectionthen and considerDirection then
						table.insert(actionsToRemove, actionName)
					end
				end
				if #actionsToRemove > 0 then 
					for _,name in ipairs(actionsToRemove) do
						if bonesNamesList ~= nil then
							local isActionHasBone = false
							for o, boneName in pairs(bonesNamesList) do
								if name == boneName then
									isActionHasBone = true
									break
								end
							end
							if not isActionHasBone then
								layer:DeleteAction(name)
							end
						else
							layer:DeleteAction(name)
						end
					end
				end
			end
		end	
	until not layer
end

function MR_TransformRigTool:CollectLayersFromGroup(moho, group, insertSelf)
	local layerList = {}
	local count = 0
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		count = count + 1
		if layer then
			if group:IsLayerValid(layer) then
				table.insert(layerList, layer)
			elseif layer == group and insertSelf then
				table.insert(layerList, layer)
			end
		end
	until not layer
	return layerList
end

function MR_TransformRigTool:CollectLayersFromSelectedGroups(moho, insertSelf)
	local layerList = {}
	local count = 0
	repeat
		local layer = moho.document:LayerByAbsoluteID(count)
		count = count + 1
		if layer then
			if layer:IsAncestorSelected() then
				table.insert(layerList, layer)
			elseif layer:SecondarySelection() and insertSelf then
				table.insert(layerList, layer)
			end
		end
	until not layer
	return layerList
end

function MR_TransformRigTool:ScanLayersReccursion(moho, group)
	for i, l in ipairs(self.templayersList) do
		self.templayersList[i] = nil
	end
	if group:IsGroupType() then
		table.insert(self.templayersList, group)
		self:ScanGroupReccursion(moho, group)
	end
	return self.templayersList
end

function MR_TransformRigTool:ScanGroupReccursion(moho, group)
	local groupLayer = moho:LayerAsGroup(group)
	for i=0, groupLayer:CountLayers()-1 do
		local layer = group:Layer(i)
		if layer:IsGroupType() then
			table.insert(self.templayersList, layer)
			self:ScanGroupReccursion(moho, layer) -- recursion
		else
			table.insert(self.templayersList, layer)
		end
	end
end

function MR_TransformRigTool:TranslateOrigins(moho, offset)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local layerScale = LM.Vector3:new_local()
		layerScale:Set(layer.fScale.value)
		if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation then
			if not self:CheckLayerParentalFlip(moho, layer) then
				local newOriginPos = LM.Vector2:new_local()
				local origin = layer:Origin()
				local newOffset = LM.Vector2:new_local()
				newOffset:Set(offset)
				
				local zeroOffset = LM.Vector2:new_local()
				newOffset:Set(self:GetLocalPos(moho, layer, newOffset))
				zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
				newOffset:Set(newOffset - zeroOffset)
				newOriginPos:Set(origin + newOffset)
				self:SetOrigin(moho, layer, newOriginPos)
			end	
		end	
	end
	for k, id in ipairs(self.groupsToChange.group) do
		local layer = moho.document:LayerByAbsoluteID(id)
		local layerScale = LM.Vector3:new_local()
		layerScale:Set(layer.fScale.value)
		if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation then
			if not self:CheckLayerParentalFlip(moho, layer) then
				local newOriginPos = LM.Vector2:new_local()
				local origin = layer:Origin()
				local newOffset = LM.Vector2:new_local()
				newOffset:Set(offset)
				
				local zeroOffset = LM.Vector2:new_local()
				newOffset:Set(self:GetLocalPos(moho, layer, newOffset))
				zeroOffset:Set(self:GetLocalPos(moho, layer, zeroOffset))
				newOffset:Set(newOffset - zeroOffset)
				newOriginPos:Set(origin + newOffset)
				self:SetOrigin(moho, layer, newOriginPos)
			end	
		end	
	end
end

function MR_TransformRigTool:ScaleOrigins(moho, center, scaling)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local layerScale = LM.Vector3:new_local()
		local layerAngle = layer.fRotationZ.value
		layerScale:Set(layer.fScale.value)
		if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation then
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
			local dif = LM.Vector2:new_local()
			local newOriginPos = LM.Vector2:new_local()
			local origin = layer:Origin()
			dif:Set(origin - centerVec)
			newOriginPos:Set((dif * scaling.x) + centerVec)
			self:SetOrigin(moho, layer, newOriginPos)
		end	
	end
	for k, id in ipairs(self.groupsToChange.group) do
		local layer = moho.document:LayerByAbsoluteID(id)
		local layerScale = LM.Vector3:new_local()
		layerScale:Set(layer.fScale.value)
		if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation then
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(self.lastCenter)
			centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
			local dif = LM.Vector2:new_local()
			local newOriginPos = LM.Vector2:new_local()
			local origin = layer:Origin()
			dif:Set(origin - centerVec)
			newOriginPos:Set((dif * scaling.x) + centerVec)
			self:SetOrigin(moho, layer, newOriginPos)
		end	
	end
end

function MR_TransformRigTool:RotateOrigins(moho, center, angle)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		if layer:GetFollowingLayer() == nil and self.followPathAdaptation or not self.followPathAdaptation then
			local layerScale = LM.Vector3:new_local()
			layerScale:Set(layer.fScale.value)
			local localAngle = angle
			if self:CheckLayerParentalFlip(moho, layer, true) then
				localAngle = -localAngle
			end
			
			if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation  then
				local centerVec = LM.Vector2:new_local()
				centerVec:Set(center)
				centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
				local dif = LM.Vector2:new_local()
				local newOriginPos = LM.Vector2:new_local()
				local origin = layer:Origin()
				dif:Set(origin - centerVec)
				local px = dif.x * math.cos(localAngle) - dif.y * math.sin(localAngle)
				local py = dif.x * math.sin(localAngle) + dif.y * math.cos(localAngle)
				newOriginPos:Set(px + centerVec.x, py + centerVec.y)
				self:SetOrigin(moho, layer, newOriginPos)
			end
		end	
	end
	for k, id in ipairs(self.groupsToChange.group) do
		local layer = moho.document:LayerByAbsoluteID(id)
		local layerScale = LM.Vector3:new_local()
		layerScale:Set(layer.fScale.value)
		local localAngle = angle
		if self:CheckLayerParentalFlip(moho, layer, true) then
			localAngle = -localAngle
		end
		if (layerScale.x == 1 and layerScale.y == 1 and layerScale.z == 1 and layer.fRotationZ.value == 0) or not self.protectLayerTransformation  then
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			centerVec = self:GetLocalPos(moho, layer, centerVec) --use global pos
			local dif = LM.Vector2:new_local()
			local newOriginPos = LM.Vector2:new_local()
			local origin = layer:Origin()
			dif:Set(origin - centerVec)
			local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
			local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
			newOriginPos:Set(px + centerVec.x, py + centerVec.y)
			self:SetOrigin(moho, layer, newOriginPos)
		end	
	end
end

function MR_TransformRigTool:DuplicateAndFlipSmarbone(moho)
	local mainSkel = self:FindSkeleton(moho)
	self:FindSkeletonLayer(moho)
	local skelLayer = self.skelLayer
	if not mainSkel or not skelLayer then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate smartbone alert 1'),
		'', "", 'OK')
		return
	end
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, mainSkel)
	if self.mainSkelSelectedBones ~= 1 then
		local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Duplicate smartbone alert 1'),
		self:Localize('Duplicate smartbone alert 2'), "", 'OK')
		return
	end
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local isDirectionRight = true
	local direction1
	local direction2
	moho:SetSelLayer(skelLayer)
	local selectedBoneId = mainSkel:SelectedBoneID()
	local selectedBone = mainSkel:Bone(selectedBoneId)
	local selectedBoneName = selectedBone:Name()
	local customName = false
	local dlog = MR_RenameSmartBoneDialog:new(moho, selectedBoneName)
	if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then
		return
	end
	
	if self.directionSmartbone then
		if self.rDirectionSmartbone then
			isDirectionRight = true
			direction1 = 'R'
			direction2 = 'L'
		elseif self.lDirectionSmartbone then
			isDirectionRight = false
			direction1 = 'L'
			direction2 = 'R'
		end
	elseif self.renameSmartbone then
		customName = true
	end
	
	moho.document:SetDirty()
	moho.document:PrepUndo(nil)
	local currentLayer = moho.layer
	
	moho:Copy()
	moho:SetSelLayer(topLayer)
	
	local tempSkelLayer = moho:CreateNewLayer(MOHO.LT_BONE, false)
	local tempSkel = moho:LayerAsBone(tempSkelLayer):Skeleton()
	tempSkelLayer:SetName('Temp Layer')
	moho:SetSelLayer(tempSkelLayer)
	
	moho:Paste()
	
	local tempDuplicatedBone = tempSkel:Bone(0)
	local newBoneName = ''
	if customName then
		newBoneName = self.newSmartBoneName
	else
		newBoneName = self:ChangeNameString(moho, tempDuplicatedBone:Name(), direction1, direction2)
		newBoneName = self:ChangeNameString(moho, newBoneName, string.lower(direction1), string.lower(direction2))
	end
	local duplicatSufix = self.duplicatedBodypartSuffix
	local targetName = newBoneName
	local counter = ''
	local isNameOk = true
	local sufix = ''
	repeat
		isNameOk = true
		for i = 0, mainSkel:CountBones()-1 do
			local mainSkelBone = mainSkel:Bone(i)
			if targetName..sufix..counter == mainSkelBone:Name() then
				isNameOk = false
				if counter == '' and sufix ~= '' then
					counter = 0
				end
				if sufix == '' then
					sufix = duplicatSufix
				elseif counter >= 0 then
					sufix = duplicatSufix..self.counterDelimiter
				end
				if counter ~= '' then
					counter = counter + 1
				end	
				break
			end
		end
		newBoneName = targetName..sufix..counter
	until isNameOk
	
	tempDuplicatedBone:SetName(newBoneName)
	
	self:CleanUpActions(moho, tempSkelLayer, true, isDirectionRight, nil)
	local actionNames = {}
	
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		if selectedBoneName == actionName then
			if newBoneName ~= actionName then
				tempSkelLayer:RenameAction(actionName, newBoneName)
				break
			end	
		end
	end
	
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		if selectedBoneName..' 2' == actionName then
			if newBoneName..' 2' ~= actionName then
				tempSkelLayer:RenameAction(actionName, newBoneName..' 2')
				break
			end	
		end
	end

	moho:Copy()
	moho:SetSelLayer(skelLayer)
	mainSkel:SelectNone()
	moho:Paste()
		
	local duplicatedBone = mainSkel:Bone(mainSkel:CountBones()-1)
	duplicatedBone.fParent = selectedBone.fParent
	duplicatedBone.fAnimParent:SetValue(0, selectedBone.fParent)
	duplicatedBone.fAnimPos:SetValue(0, selectedBone.fAnimPos:GetValue(0))
	duplicatedBone.fAnimAngle:SetValue(0, selectedBone.fAnimAngle:GetValue(0))

	mainSkel:SelectNone()
	duplicatedBone.fSelected = true
	
	for d=0, tempSkel:CountBones()-1 do
		tempSkel:DeleteBone(0)
	end
	local actionsToRemove = {}
	
	for i=0, tempSkelLayer:CountActions()-1 do
		local actionName = tempSkelLayer:ActionName(i)
		table.insert(actionsToRemove, actionName)
	end
	
	for l, action in ipairs(actionsToRemove) do
		tempSkelLayer:DeleteAction(action)
	end
	moho:DeleteLayer(tempSkelLayer)
	moho:SetSelLayer(currentLayer)
	moho:SetCurFrame(1)
	moho:SetCurFrame(0)
	moho.view:DrawMe()
	moho:UpdateUI()
	moho.layer:UpdateCurFrame()
end

function MR_TransformRigTool:ScaleVector2(moho, vector2, center, scaling)
	local centerVec = LM.Vector2:new_local()
	centerVec:Set(center)
	local dif = LM.Vector2:new_local()
	local newVector2 = LM.Vector2:new_local()
	dif:Set(vector2 - centerVec)
	newVector2:Set((dif * scaling.x) + centerVec)
	return newVector2
end

function MR_TransformRigTool:RotateVector2(moho, vector2, center, angle)
	local centerVec = LM.Vector2:new_local()
	local newVector2 = LM.Vector2:new_local()
	local dif = LM.Vector2:new_local()
	centerVec:Set(center)
	dif:Set(vector2 - centerVec)
	local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
	local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
	newVector2:Set(px + centerVec.x, py + centerVec.y)
	return newVector2
end

function MR_TransformRigTool:Flip(moho, direction, alt)
	self.skel = self:FindSkeleton(moho)
	local skel = self.skel
	
	self.mainSkelSelectedBones = self:CountSelectedBones(moho, skel)
	self:ScanLayers(moho, true)
	
	if (self.vectorLayersToChange.layer[1] == nil) and self.imageLayersToChange.layer[1] == nil then
		if self.patchLayersToChange.layer[1] == nil then
			if (self.transformBones and self.mainSkelSelectedBones ~= 1) or not self.transformBones then
				self.status = self:Localize('No layers selected for active mode.')
				self:UpdateWidgets(moho)
				self.blockTransformation = true
				return
			end	
		end	
	end
	
	if (self.transformBones and self.mainSkelSelectedBones ~= 1) or (self.centerId == 1 and self.mainSkelSelectedBones ~= 1) then
		self.status = self:Localize('You need to select one bone in this mode.')
		self:UpdateWidgets(moho)
		self.blockTransformation = true
		return
	end
	
	-- if self.transformBones and self.mainSkelSelectedBones ~= 1 then
		-- self.status = self:Localize('You need to select one bone in this mode.')
		-- self:UpdateWidgets(moho)
		-- self.blockTransformation = true
		-- return
	-- end
	
	-- if (self.vectorLayersToChange.layer[1] == nil) and self.imageLayersToChange.layer[1] == nil then
		-- if self.patchLayersToChange.layer[1] == nil then
			-- if not self.followPathAdaptation then 
				-- self.status = self:Localize('No layers selected for active mode.')
				-- self:UpdateWidgets(moho)
				-- self.blockTransformation = true
				-- return
			-- elseif	self.followPathGroupsToChange.group[1] == nil then
				-- self.status = self:Localize('No layers selected for active mode.')
				-- self:UpdateWidgets(moho)
				-- self.blockTransformation = true
			-- end
		-- end	
	-- end
	
	for m in pairs(self.fixedActionsList) do
		self.fixedActionsList[m] = nil
	end
	
	self.selectedMainBone = skel:Bone(skel:SelectedBoneID())
	
	moho.document:SetDirty()
	moho.document:PrepUndo(self.skelLayer)
	if self.centerId == 1 and self.mainSkelSelectedBones ~= 1 then
		self.status = self:Localize('You need to select one bone in this mode.')
		self:UpdateWidgets(moho)
		return
	end
	self:FindSkeletonLayer(moho)
	if not alt then
		self.lastSkelLayer = self.skelLayer
	end	
	
	if self.lastSkelLayer == nil then
		self.status = self:Localize('No last flip to repeat')
		self:UpdateWidgets(moho)
		return
	end
	
	local center = LM.Vector2:new_local()
	local bone = skel:Bone(skel:SelectedBoneID())
	if self.centerId == 0 then -- custom center
		center:Set(self.centerVec)
	elseif self.centerId == 1 then -- selected bone
		local bonePos = LM.Vector2:new_local()
		bonePos:Set(self:GetGlobalBonePos(moho, bone))
		center:Set(bonePos)
	end	
	
	if alt and self.lastFlipCenter ~= nil then
		center:Set(self.lastFlipCenter)
	end
	if self.transformPoints then
		self:FlipPoints(moho, direction, center, alt)
	end	
	
	---------- Flip groups origin ----------
	if self.transformOrigin then
		for k, id in ipairs(self.groupsToChange.group) do
			local group = moho:LayerAsGroup(moho.document:LayerByAbsoluteID(id))
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			local dif = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			
			local origin = self:GetGlobalPos(moho, group, group:Origin())
			if direction then
				dif:Set(centerVec.x - origin.x, 0)
				newPos:Set(centerVec.x + dif.x, origin.y)
			else
				dif:Set(0, centerVec.y - origin.y)
				newPos:Set(origin.x, centerVec.y + dif.y )
			end
			newPos = self:GetLocalPos(moho, group, newPos)
			self:SetOrigin(moho, group, newPos)
		end
	end
	if self.ignoreRefLayers then
		for k, layer in ipairs(self.refLayersList) do
			layer:MarkReferenceOutdated()
		end	
	end	
	
	if self.transformBones and self.mainSkelSelectedBones == 1 then
		self:FlipBoneChain(moho, direction, false, alt)
		if self.mainSkelSelectedBones == 1 then
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
			local bone = skel:Bone(skel:SelectedBoneID())
			local dif = LM.Vector2:new_local()
			local bonePos = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			local globalOffset = LM.Vector2:new_local()
			local parentBoneMatrix = LM.Matrix:new_local()
			local lastSkelMatrix = LM.Matrix:new_local()
			local skelMatrix = LM.Matrix:new_local()
			local invertedSkelMatrix = LM.Matrix:new_local()
			self.skelLayer:GetFullTransform(0, skelMatrix, nil)
			invertedSkelMatrix:Set(skelMatrix)
			invertedSkelMatrix:Invert()
			
			if self.lastSkelLayer ~= nil then
				self.lastSkelLayer:GetFullTransform(0, lastSkelMatrix, nil)
			end
			local invertedLastSkelMatrix = LM.Matrix:new_local()
			invertedLastSkelMatrix:Set(lastSkelMatrix)
			invertedLastSkelMatrix:Invert()
			
			bonePos:Set(bone.fAnimPos:GetValue(0))
			if bone.fParent > -1 then
				parentBoneMatrix:Set(skel:Bone(bone.fParent).fMovedMatrix)
				parentBoneMatrix:Transform(bonePos)
			end
			
			if alt and not self.useGlobalFlip then
				skelMatrix:Transform(bonePos)
				invertedLastSkelMatrix:Transform(bonePos)
				centerVec:Set(center)
				invertedLastSkelMatrix:Transform(centerVec)
			elseif self.useGlobalFlip then
				skelMatrix:Transform(bonePos)
				centerVec:Set(center)
			end
			
			if direction then
				dif:Set(centerVec.x - bonePos.x, 0)
				newPos:Set(centerVec.x + dif.x, bonePos.y)
			else
				dif:Set(0, centerVec.y - bonePos.y)
				newPos:Set(bonePos.x, centerVec.y + dif.y )
			end
			
			if alt and not self.useGlobalFlip then
				lastSkelMatrix:Transform(newPos)
				invertedSkelMatrix:Transform(newPos)
			elseif self.useGlobalFlip then
				invertedSkelMatrix:Transform(newPos)
			end
			
			globalOffset:Set(self:GetGlobalPos(moho, self.skelLayer, newPos) - self:GetGlobalBonePos(moho, bone))
			
			if bone.fParent > -1 then
				parentBoneMatrix:Invert()
				parentBoneMatrix:Transform(newPos)
			end

			bone.fAnimPos:SetValue(0, newPos)
			self:TranslateBoneInActions(moho, bone, globalOffset)
		end
		
		if self.transformTargetBones and self.mainSkelSelectedBones == 1 then
			local selectedBone = skel:Bone(skel:SelectedBoneID())
			self:FindTargetBones(moho, selectedBone, true)
		end
		
		if self.transformVitruvianBones and self.mainSkelSelectedBones == 1 and self.isVitruvianBonesAvaible then
			local selectedBoneId = skel:SelectedBoneID()
			local selectedBone = skel:Bone(skel:SelectedBoneID())
			local centerVec = LM.Vector2:new_local()
			centerVec:Set(center)
			centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
			if skel:IsBoneInAGroup(selectedBoneId) then
				local vitruvianGroup = skel:GroupForBone(selectedBoneId, false)
				for i=0, vitruvianGroup:CountBones()-1 do
					local bone = vitruvianGroup:Bone(i)
					if skel:BoneID(bone) ~= selectedBoneId then
						skel:SelectNone()
						bone.fSelected = true
						self:FlipBoneChain(moho, direction, false)
						local dif = LM.Vector2:new_local()
						local bonePos = LM.Vector2:new_local()
						local newPos = LM.Vector2:new_local()
						local globalOffset = LM.Vector2:new_local()
						local parentBoneMatrix = LM.Matrix:new_local()
						
						local lastSkelMatrix = LM.Matrix:new_local()
						local skelMatrix = LM.Matrix:new_local()
						local invertedSkelMatrix = LM.Matrix:new_local()
						self.skelLayer:GetFullTransform(0, skelMatrix, nil)
						invertedSkelMatrix:Set(skelMatrix)
						invertedSkelMatrix:Invert()
						
						if self.lastSkelLayer ~= nil then
							self.lastSkelLayer:GetFullTransform(0, lastSkelMatrix, nil)
						end
						local invertedLastSkelMatrix = LM.Matrix:new_local()
						invertedLastSkelMatrix:Set(lastSkelMatrix)
						invertedLastSkelMatrix:Invert()
						
						bonePos:Set(bone.fAnimPos:GetValue(0))
						if bone.fParent > -1 then
							parentBoneMatrix:Set(skel:Bone(bone.fParent).fMovedMatrix)
							parentBoneMatrix:Transform(bonePos)
						end
						
						if alt and not self.useGlobalFlip then
							skelMatrix:Transform(bonePos)
							invertedLastSkelMatrix:Transform(bonePos)
							centerVec:Set(center)
							invertedLastSkelMatrix:Transform(centerVec)
						elseif self.useGlobalFlip then
							skelMatrix:Transform(bonePos)
							centerVec:Set(center)
						end

						if direction then
							dif:Set(centerVec.x - bonePos.x, 0)
							newPos:Set(centerVec.x + dif.x, bonePos.y)
						else
							dif:Set(0, centerVec.y - bonePos.y)
							newPos:Set(bonePos.x, centerVec.y + dif.y )
						end
						
						if alt and not self.useGlobalFlip then
							lastSkelMatrix:Transform(newPos)
							invertedSkelMatrix:Transform(newPos)
						elseif self.useGlobalFlip then
							invertedSkelMatrix:Transform(newPos)
						end
						
						globalOffset:Set(self:GetGlobalPos(moho, self.skelLayer, newPos) - self:GetGlobalBonePos(moho, bone))
						if bone.fParent > -1 then
							parentBoneMatrix:Invert()
							parentBoneMatrix:Transform(newPos)
						end
						bone.fAnimPos:SetValue(0, newPos)
						self:TranslateBoneInActions(moho, bone, globalOffset)
						if self.transformTargetBones and self.mainSkelSelectedBones == 1 then
							local selectedBone = skel:Bone(skel:SelectedBoneID())
							self:FindTargetBones(moho, bone, false)
						end
					end
				end	
				skel:SelectNone()
				selectedBone.fSelected = true
			end
		end
		if self.transformTargetBones and self.mainSkelSelectedBones == 1 then
			self:FlipTargetBones(moho, center, direction, alt)
		end	
	end
	if self.transformImageLayers then
		for i, id in ipairs(self.imageLayersToChange.layer) do
			local layer = moho.document:LayerByAbsoluteID(id)
			local isLayerinFollowPathGroup = false
			if self.followPathAdaptation then
				for i, groupId in ipairs(self.followPathGroupsToChange.group) do
					if layer:Parent() then
						local parentLayerID = moho.document:LayerAbsoluteID(layer:Parent())
						if parentLayerID == groupId then
							isLayerinFollowPathGroup = true
						end
					end
				end
			end
			if not isLayerinFollowPathGroup then
				self:FlipLayer(moho, layer, center, direction, alt)
			end	
		end
	end
	
	if self.followPathAdaptation then
		for i, id in ipairs(self.followPathGroupsToChange.group) do
			local group = moho.document:LayerByAbsoluteID(id)
			if self.followPathGroupsToChange.transform[i] then
				self:FlipLayer(moho, group, center, direction, alt)
			end	
		end
	end
	
	if self.transformPatchLayers then
		for i, id in ipairs(self.patchLayersToChange.layer) do
			local layer = moho.document:LayerByAbsoluteID(id)
			self:FlipLayer(moho, layer, center, direction, alt)
		end
	end
	
	local totalActions = #self.fixedActionsList
	local actionsStr = self:Localize(' actions were updated.')
	if totalActions == 1 then
		actionsStr = self:Localize(' action was updated.')
	end
	if totalActions  > 0 then
		self.status = totalActions.. actionsStr
	else
		self.status = self:Localize('Actions did not need to be updated')
	end
	
	if self.transformPoints then
		self:RefreshCachedLayers(moho)
	end
	
	self.lastFlipCenter = LM.Vector2:new_local()
	self.lastFlipCenter:Set(center)
	moho:UpdateUI()	
	moho.layer:UpdateCurFrame()
	moho:SetCurFrame(1)
	moho:SetCurFrame(0)
end

function MR_TransformRigTool:AdjustStrokesWidth(moho, scaling)
	for k, id in ipairs(self.vectorLayersToStrokeChange) do
		local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
		local mesh = layer:Mesh()
		if mesh == nil then
			return
		end
		if self.useLineWidthInsteadStrokeWidth then
			for i = 0, mesh:CountPoints() - 1 do
				local point = mesh:Point(i)
				local newValue = point.fWidth:GetValue(0) * scaling
				point.fWidth:SetValue(0, newValue)
				for actionID = 0, point.fWidth:CountActions() - 1 do
					local action = moho:ChannelAsAnimVal(point.fWidth:Action(actionID))
					local actionName = point.fWidth:ActionName(actionID)
					if action ~= nil then
						for keyID = 0, action:CountKeys() - 1 do
							local actionFrame = action:GetKeyWhen(keyID)
							if (actionFrame > 0) then
								local newValue = action:GetValue(actionFrame) * scaling
								action:SetValue(actionFrame, newValue)
								self:CollectLog(nil, actionName)
							end
						end
					end
				end
			end	
		else
			for i = 0, mesh:CountShapes() - 1 do
				local shape = mesh:Shape(i)
				if (shape ~= nil) then
					local lineWidth = shape.fMyStyle.fLineWidth * moho.document:Height()
					shape.fMyStyle.fLineWidth = LM.Clamp((lineWidth * scaling), 0.25, 256) / moho.document:Height()
				end
			end
		end
	end
end

function MR_TransformRigTool:ScaleImagesInActions(moho, center, scaleValue)
	for k, id in ipairs(self.imageLayersToChange.layer) do
		local imagelayer = moho.document:LayerByAbsoluteID(id)
		for actionID = 0, imagelayer.fScale:CountActions() - 1 do
			local action = moho:ChannelAsAnimVec3(imagelayer.fScale:Action(actionID))
			local actionName = imagelayer.fScale:ActionName(actionID)
			if action ~= nil then
				for keyID = 0, action:CountKeys() - 1 do
					local actionFrame = action:GetKeyWhen(keyID)
					if (actionFrame > 0) then
						local newScale = LM.Vector3:new_local()
						local layerScale = action:GetValue(actionFrame)
						if self.scalingMode == 1 then -- uniform
							newScale:Set(layerScale * scaleValue.x)
						elseif self.scalingMode == 2 then -- h
							newScale:Set(layerScale.x * scaleValue.x, layerScale.y, layerScale.z)
						elseif self.scalingMode == 3 then -- v
							newScale:Set(layerScale.x, layerScale.y  * scaleValue.y, layerScale.z)
						end	
						action:SetValue(actionFrame, newScale)
						if layerScale ~= newScale then
							self:CollectLog(nil, actionName)
						end	
					end
				end
			end	
		end
	end	
end

function MR_TransformRigTool:RotateImagesInActions(moho, center, angleValue)
	for k, id in ipairs(self.imageLayersToChange.layer) do
		local imagelayer = moho.document:LayerByAbsoluteID(id)
		local localAngle = angleValue
		if self.imageLayersToChange.parentalFlip[k] then
			localAngle = -localAngle
		end
		for actionID = 0, imagelayer.fRotationZ:CountActions() - 1 do
			local action = moho:ChannelAsAnimVal(imagelayer.fRotationZ:Action(actionID))
			local actionName = imagelayer.fRotationZ:ActionName(actionID)
			if action ~= nil then
				for keyID = 0, action:CountKeys() - 1 do
					local actionFrame = action:GetKeyWhen(keyID)
					if (actionFrame > 0) then
						local layerAngle = action:GetValue(actionFrame)
						local newAngle = layerAngle + localAngle
						action:SetValue(actionFrame, newAngle)
						if layerAngle ~= newAngle then
							self:CollectLog(nil, actionName)
						end	
					end
				end
			end	
		end
	end	
end

function MR_TransformRigTool:TranslateImagesInActions(moho, offset, mode, center)
	for k, id in ipairs(self.imageLayersToChange.layer) do
		local layer = moho.document:LayerByAbsoluteID(id)
		local localOffset = LM.Vector2:new_local()
		local zeroOffset = LM.Vector2:new_local()
		if mode == 0 then
			localOffset:Set(offset)
			
			if layer:Parent() then
				localOffset:Set(self:GetLocalPos(moho, layer:Parent(), offset))
				zeroOffset:Set(self:GetLocalPos(moho, layer:Parent(), zeroOffset))
				localOffset:Set(localOffset - zeroOffset)
			end	
		end
		for actionID = 0, layer.fTranslation:CountActions() - 1 do
			local action = moho:ChannelAsAnimVec3(layer.fTranslation:Action(actionID))
			local actionName = layer.fTranslation:ActionName(actionID)
			if action ~= nil then
				for keyID = 0, action:CountKeys() - 1 do
					local actionFrame = action:GetKeyWhen(keyID)
					if (actionFrame > 0) then
						local newPos = LM.Vector3:new_local()
						local layerTranslation = action:GetValue(actionFrame)
						if mode == 0 then
							newPos:Set(layerTranslation.x + localOffset.x, layerTranslation.y + localOffset.y, layerTranslation.z)
						elseif mode == 1 then	
							newPos:Set(layerTranslation + (action:GetValue(0) - self.imageLayersToChange.position[k]))
						end
						action:SetValue(actionFrame, newPos)
						if layerTranslation ~= newPos then
							self:CollectLog(nil, actionName)
						end	
					end
				end
			end	
		end
	end	
end

function MR_TransformRigTool:FlipTargetBones(moho, center, direction, alt)
	local skel = self.skel
	local selectedBone = skel:Bone(skel:SelectedBoneID())
	local centerVec = LM.Vector2:new_local()
	centerVec:Set(center)
	centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
	for n, targetBone in ipairs(self.targetBonesList) do
		if targetBone ~= self.selectedMainBone then
			skel:SelectNone()
			targetBone.fSelected = true
			self:FlipBoneChain(moho, direction, true)
			local dif = LM.Vector2:new_local()
			local bonePos = LM.Vector2:new_local()
			local newPos = LM.Vector2:new_local()
			local globalOffset = LM.Vector2:new_local()
			local parentBoneMatrix = LM.Matrix:new_local()
			
			local lastSkelMatrix = LM.Matrix:new_local()
			local skelMatrix = LM.Matrix:new_local()
			local invertedSkelMatrix = LM.Matrix:new_local()
			self.skelLayer:GetFullTransform(0, skelMatrix, nil)
			invertedSkelMatrix:Set(skelMatrix)
			invertedSkelMatrix:Invert()
			
			if self.lastSkelLayer ~= nil then
				self.lastSkelLayer:GetFullTransform(0, lastSkelMatrix, nil)
			end
			local invertedLastSkelMatrix = LM.Matrix:new_local()
			invertedLastSkelMatrix:Set(lastSkelMatrix)
			invertedLastSkelMatrix:Invert()
			
			bonePos:Set(targetBone.fAnimPos:GetValue(0))
			if targetBone.fParent > -1 then
				parentBoneMatrix:Set(skel:Bone(targetBone.fParent).fMovedMatrix)
				parentBoneMatrix:Transform(bonePos)
			end
			
			if alt and not self.useGlobalFlip then
				skelMatrix:Transform(bonePos)
				invertedLastSkelMatrix:Transform(bonePos)
				centerVec:Set(center)
				invertedLastSkelMatrix:Transform(centerVec)
			elseif self.useGlobalFlip then
				skelMatrix:Transform(bonePos)
				centerVec:Set(center)
			end
				
			if direction then
				dif:Set(centerVec.x - bonePos.x, 0)
				newPos:Set(centerVec.x + dif.x, bonePos.y)
			else
				dif:Set(0, centerVec.y - bonePos.y)
				newPos:Set(bonePos.x, centerVec.y + dif.y )
			end
			
			if alt and not self.useGlobalFlip then
				lastSkelMatrix:Transform(newPos)
				invertedSkelMatrix:Transform(newPos)
			elseif self.useGlobalFlip then
				invertedSkelMatrix:Transform(newPos)
			end
			
			globalOffset:Set(self:GetGlobalPos(moho, self.skelLayer, newPos) - self:GetGlobalBonePos(moho, targetBone))
			if targetBone.fParent > -1 then
				parentBoneMatrix:Invert()
				parentBoneMatrix:Transform(newPos)
			end

			targetBone.fAnimPos:SetValue(0, newPos)
			self:TranslateBoneInActions(moho, targetBone, globalOffset)
		end	
	end	

	skel:SelectNone()
	selectedBone.fSelected = true
end

function MR_TransformRigTool:TransformTargetBones(moho, alt)
	local skel = self.skel
	local selectedBone = skel:Bone(skel:SelectedBoneID())
	self:FindTargetBones(moho, selectedBone, true)
	for n, targetBone in ipairs(self.targetBonesList) do
		if targetBone ~= self.selectedMainBone then
			skel:SelectNone()
			targetBone.fSelected = true
			self:RepeatTransformation(moho, alt, true)
		end	
	end
	skel:SelectNone()
	selectedBone.fSelected = true
end

function MR_TransformRigTool:TranslateVitruvianBones(moho, alt)
	local skel = self.skel
	local selectedBoneId = skel:SelectedBoneID()
	local selectedBone = skel:Bone(selectedBoneId)
	if skel:IsBoneInAGroup(selectedBoneId) then
		local vitruvianGroup = skel:GroupForBone(selectedBoneId, false)
		local vitruvianGroupStatus = vitruvianGroup.fEnabled
		vitruvianGroup.fEnabled = false
		for i=0, vitruvianGroup:CountBones()-1 do
			local bone = vitruvianGroup:Bone(i)
			if skel:BoneID(bone) ~= selectedBoneId then
				skel:SelectNone()
				bone.fSelected = true
				self:RepeatTransformation(moho, alt, true)
				if self.transformTargetBones then
					self:TransformTargetBones(moho, alt)
				end
			end
		end	
		vitruvianGroup.fEnabled = vitruvianGroupStatus
		skel:SelectNone()
		selectedBone.fSelected = true
	end
end

function MR_TransformRigTool:ScaleVitruvianBones(moho, alt)
	local skel = self.skel
	local selectedBoneId = skel:SelectedBoneID()
	local selectedBone = skel:Bone(selectedBoneId)
	local scaling = LM.Vector2:new_local()
	scaling:Set(self.lastScaling)
	if alt then
		local scalePercentageX = 100 / (1 / scaling.x)
		local scalePercentageY = 100 / (1 / scaling.y)
		scaling:Set(1/((1/100) * scalePercentageX), 1/((1/100) * scalePercentageY))
	end
	if skel:IsBoneInAGroup(selectedBoneId) then
		local vitruvianGroup = skel:GroupForBone(selectedBoneId, false)
		local vitruvianGroupStatus = vitruvianGroup.fEnabled
		vitruvianGroup.fEnabled = false
		for i=0, vitruvianGroup:CountBones()-1 do
			local bone = vitruvianGroup:Bone(i)
			if skel:BoneID(bone) ~= selectedBoneId then
				skel:SelectNone()
				bone.fSelected = true
				self:RepeatTransformation(moho, alt, true)
			end
		end	
		vitruvianGroup.fEnabled = vitruvianGroupStatus
		skel:SelectNone()
		selectedBone.fSelected = true
	end
end

function MR_TransformRigTool:RotateVitruvianBones(moho, alt)
	local skel = self.skel
	local angle = self.lastAngle
	if alt then
		angle = -angle
	end
	local selectedBoneId = skel:SelectedBoneID()
	local selectedBone = skel:Bone(selectedBoneId)
	if skel:IsBoneInAGroup(selectedBoneId) then
		local vitruvianGroup = skel:GroupForBone(selectedBoneId, false)
		local vitruvianGroupStatus = vitruvianGroup.fEnabled
		vitruvianGroup.fEnabled = false
		for i=0, vitruvianGroup:CountBones()-1 do
			local bone = vitruvianGroup:Bone(i)
			if skel:BoneID(bone) ~= selectedBoneId then
				skel:SelectNone()
				bone.fSelected = true
				self:RepeatTransformation(moho, alt, true)
			end
		end	
		vitruvianGroup.fEnabled = vitruvianGroupStatus
		skel:SelectNone()
		selectedBone.fSelected = true
	end
end

function MR_TransformRigTool:CheckLayerParentalFlip(moho, layer, selfCheck)
	local targetLayer
	if selfCheck then
		targetLayer = layer
	else
		if layer:Parent() then
			targetLayer = layer:Parent()
		else
			targetLayer = layer
		end
	end
	local flipFactor = false
	repeat
		local flipH = targetLayer.fFlipH.value
		local flipV = targetLayer.fFlipV.value
		if ((flipH and not flipV) or (flipV and not flipH)) then
			flipFactor = not flipFactor
		end
		targetLayer = targetLayer:Parent()
	until targetLayer == nil
	return flipFactor
end

function MR_TransformRigTool:TranslateBoneInActions(moho, bone, offset)
	local skel = self.skel
	local localOffset = LM.Vector2:new_local()
	local zeroOffset = LM.Vector2:new_local()
	zeroOffset:Set(0, 0)
	localOffset:Set(self:GetLocalPos(moho, self.skelLayer, offset))
	zeroOffset:Set(self:GetLocalPos(moho, self.skelLayer, zeroOffset))
	localOffset:Set(localOffset - zeroOffset)
	
	for actionID = 0, bone.fAnimPos:CountActions() - 1 do
		local actionChannel = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
		local actionName = bone.fAnimPos:ActionName(actionID)
		if actionChannel ~= nil then
			for keyID = 0, actionChannel:CountKeys() - 1 do
				local actionFrame = actionChannel:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					if bone.fParent >= 0 then
						self.skelLayer:ActivateAction(actionName)
						moho:SetCurFrame(actionFrame)
						local bonePos = LM.Vector2:new_local()	
						local originalPos = LM.Vector2:new_local()	
						bonePos:Set(actionChannel:GetValue(actionFrame))
						originalPos:Set(bonePos)
						local parentBone = skel:Bone(bone.fAnimParent:GetValue(0))
						local parentMatrix = parentBone.fMovedMatrix
						parentMatrix:Transform(bonePos)
						bonePos:Set(bonePos + localOffset)
						local invertedMatrix = LM.Matrix:new_local()
						invertedMatrix:Set(parentMatrix)
						invertedMatrix:Invert()
						local newPos = LM.Vector2:new_local()
						newPos:Set(bonePos)
						invertedMatrix:Transform(newPos)
						actionChannel:SetValue(actionFrame, newPos)
						if originalPos ~= newPos then
							self:CollectLog(nil, actionName)
						end	
					else
						actionChannel:SetValue(actionFrame, actionChannel:GetValue(actionFrame) + localOffset)
					end
				end	
			end	
			self.skelLayer:ActivateAction(nil)
			moho:SetCurFrame(0)
		end			
	end	
	
end

function MR_TransformRigTool:FindTargetBones(moho, bone, startNewList)
	local skel = self.skel
	local boneId = skel:BoneID(bone)
	if startNewList then
		for i, l in ipairs(self.targetBonesList) do
			self.targetBonesList[i] = nil
		end	
	end	

	for i = 0, skel:CountBones() - 1 do
		if skel:IsBoneParent(i, boneId) or i == boneId then
			local childBone = skel:Bone(i)
			for b = 0, skel:CountBones() - 1 do
				local isTargetFound = false
				local targetBoneCandidat = skel:Bone(b)
				if targetBoneCandidat.fParent == -1 then
					if childBone.fTargetBone.value == b then
						isTargetFound = true
					else
						for actionID = 0, childBone.fTargetBone:CountActions() - 1 do
							local actionChannel = moho:ChannelAsAnimVal(childBone.fTargetBone:Action(actionID))
							if actionChannel ~= nil then
								for keyID = 0, actionChannel:CountKeys() - 1 do
									local actionFrame = actionChannel:GetKeyWhen(keyID)
									if (actionFrame > 0) then
										if actionChannel:GetValue(actionFrame) == b then
											isTargetFound = true
											break
										end
									end
								end
							end
						end	
					end
					if isTargetFound then	
						local isBoneNotInList = true
						for k, tBone in pairs(self.targetBonesList) do
							if tBone == targetBoneCandidat then
								isBoneNotInList = false
								break
							end
						end
						if isBoneNotInList then
							table.insert(self.targetBonesList, targetBoneCandidat)
							break
						end	
					end
				end	
			end	
		end
	end
end

function MR_TransformRigTool:RotateBoneInActions(moho, bone, angle)
	for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
		local action = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
		local actionName = bone.fAnimAngle:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local originalVal = action:GetValue(actionFrame)
					action:SetValue(actionFrame, action:GetValue(actionFrame) + angle)
					if originalVal ~= originalVal + angle then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end
	end
end

function MR_TransformRigTool:AskForRejoinDemensions(moho)
	local isRejoint = false
	local ans = LM.GUI.Alert(LM.GUI.ALERT_QUESTION, self:Localize('Channels with separated dimensions were found.'),
	self:Localize('Do You want to rejoin dimensions?'), "",  self:Localize('Rejoin Dimensions'), self:Localize('Cancel'),"")
	if ans == 0 then
		isRejoint = true
	end
	return isRejoint
end

function MR_TransformRigTool:SplitChannel(moho, channel, isAllowRejoin)
	if not isAllowRejoin then
		isAllowRejoin = self:AskForRejoinDemensions(moho)
		if not isAllowRejoin then
			return false
		else
			channel:SplitDimensions(false)
			return true
		end
	else
		channel:SplitDimensions(false)
		return true
	end
end

function MR_TransformRigTool:CheckForSplitDimensions(moho)
	self.skel = self:FindSkeleton(moho)
	if self.skel == nil then
		return
	end
	moho.document:PrepUndo(self.skelLayer)
	moho.document:SetDirty()
	self:ScanLayers(moho)
	local isAllowRejoin = false
	local skel = self.skel
	self:FindSkeletonLayer(moho)
	if self.transformBones then
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			if bone.fAnimPos:AreDimensionsSplit() then
				isAllowRejoin = self:SplitChannel(moho, bone.fAnimPos, isAllowRejoin)
				if not isAllowRejoin then
					return
				end
			end
			for act=0, bone.fAnimPos:CountActions()-1 do
				local actName = bone.fAnimPos:ActionName(act)
				local isSmartBone = self.skelLayer:IsSmartBoneAction(actName)
				if isSmartBone then
					local actionChannel = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(act))
					if actionChannel:AreDimensionsSplit() then
						isAllowRejoin = self:SplitChannel(moho, actionChannel, isAllowRejoin)
						if not isAllowRejoin then
							return
						end
					end
				end
			end	
		end
	end
	if self.transformPoints then
		for k, id in ipairs(self.vectorLayersToChange.layer) do
			local layer = moho:LayerAsVector(moho.document:LayerByAbsoluteID(id))
			local mesh = layer:Mesh()
			for i = 0, mesh:CountPoints()-1 do
				local point = mesh:Point(i)
				if point.fAnimPos:AreDimensionsSplit() then
					isAllowRejoin = self:SplitChannel(moho, point.fAnimPos, isAllowRejoin)
					if not isAllowRejoin then
						return
					end
				end
				for act=0, point.fAnimPos:CountActions()-1 do
					local actName = point.fAnimPos:ActionName(act)
					local isSmartBone = self.skelLayer:IsSmartBoneAction(actName)
					if isSmartBone then
						local actionChannel = moho:ChannelAsAnimVec2(point.fAnimPos:Action(act))
						if actionChannel:AreDimensionsSplit() then
							isAllowRejoin = self:SplitChannel(moho, actionChannel, isAllowRejoin)
							if not isAllowRejoin then
								return
							end
						end
					end
				end
			end
		end
	end	
	if self.transformImageLayers then
		for k, id in ipairs(self.imageLayersToChange.layer) do
			local layer = moho.document:LayerByAbsoluteID(id)
			if layer.fTranslation:AreDimensionsSplit() then
				isAllowRejoin = self:SplitChannel(moho, layer.fTranslation, isAllowRejoin)
				if not isAllowRejoin then
					return
				end
			end
			if layer.fScale:AreDimensionsSplit() then
				isAllowRejoin = self:SplitChannel(moho, layer.fScale, isAllowRejoin)
				if not isAllowRejoin then
					return
				end
			end
			for act=0, layer.fTranslation:CountActions()-1 do
				local actName = layer.fTranslation:ActionName(act)
				local isSmartBone = self.skelLayer:IsSmartBoneAction(actName)
				if isSmartBone then
					local actionChannel = moho:ChannelAsAnimVec3(layer.fTranslation:Action(act))
					if actionChannel:AreDimensionsSplit() then
						isAllowRejoin = self:SplitChannel(moho, actionChannel, isAllowRejoin)
						if not isAllowRejoin then
							return
						end
					end
				end
			end
			for act=0, layer.fScale:CountActions()-1 do
				local actName = layer.fScale:ActionName(act)
				local isSmartBone = self.skelLayer:IsSmartBoneAction(actName)
				if isSmartBone then
					local actionChannel = moho:ChannelAsAnimVec3(layer.fScale:Action(act))
					if actionChannel:AreDimensionsSplit() then
						isAllowRejoin = self:SplitChannel(moho, actionChannel, isAllowRejoin)
						if not isAllowRejoin then
							return
						end
					end
				end
			end
		end
	end
	
	if not isAllowRejoin then
		local ans = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('No channels with separated dimensions were found.'),
		"", "",  "OK", "","")
	end
	moho:UpdateUI()
end

function MR_TransformRigTool: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

function MR_TransformRigTool:AddBonesToList(moho)
	local skel = moho:Skeleton()
	for i, b in pairs(self.bonesLinkList.bone) do
		self.bonesLinkList[i] = nil
		self.bonesLinkList.bone[i] = nil
		self.bonesLinkList.parentBone[i] = nil
	end
	local selectedBones = {}
	for i = 0, moho:CountBones()-1 do
		local bone = skel:Bone(i)
		if bone.fSelected then
			table.insert(selectedBones, i)
			if bone.fParent > -1 then
				table.insert(self.bonesLinkList.bone, i)
				table.insert(self.bonesLinkList.parentBone, bone.fParent)
			end	
		end
	end
	for k, selectedBone in ipairs(selectedBones) do
		for i = 0, moho:CountBones()-1 do
			local bone = skel:Bone(i)
			if bone.fParent == selectedBone then
				table.insert(self.bonesLinkList.bone, i)
				table.insert(self.bonesLinkList.parentBone, selectedBone)
			end
		end
	end
end

function MR_TransformRigTool:UnlinkBones(moho)
	for i, boneID in ipairs(self.bonesLinkList.bone) do
		self:ReparentBones(moho, boneID, -1)
	end
end

function MR_TransformRigTool:RelinkBones(moho)
	local skel = moho:Skeleton()
	for i, boneID in ipairs(self.bonesLinkList.bone) do
		local skel = moho:Skeleton()
		local bone = skel:Bone(boneID)
		local parentBone = skel:Bone(self.bonesLinkList.parentBone[i])
		if bone ~= nil and parentBone ~= nil then
			self:ReparentBones(moho, boneID, self.bonesLinkList.parentBone[i])
		end
	end
	for i, b in pairs(self.bonesLinkList.bone) do
		self.bonesLinkList[i] = nil
		self.bonesLinkList.bone[i] = nil
		self.bonesLinkList.parentBone[i] = nil
	end
end

function MR_TransformRigTool:ReparentBones(moho, boneID, parentBoneID)
	local skel = moho:Skeleton()
	local bone = skel:Bone(boneID)
	if (parentBoneID ~= -1 and skel:Bone(parentBoneID) == nil) or bone == nil then
		return
	end
	
	local v1 = LM.Vector2:new_local()
	local v2 = LM.Vector2:new_local()
	v1:Set(0, 0)
	if (bone:IsZeroLength()) then
		v2:Set(0.1, 0)
	else
		v2:Set(bone.fLength, 0)
	end
	bone.fMovedMatrix:Transform(v1)
	bone.fMovedMatrix:Transform(v2)

	if parentBoneID >= 0 then
		local invMatrix = LM.Matrix:new_local()
		local parent = skel:Bone(parentBoneID)
		invMatrix:Set(parent.fMovedMatrix)
		invMatrix:Invert()
		invMatrix:Transform(v1)
		invMatrix:Transform(v2)
	end

	bone.fAnimPos:SetValue(0, v1)
	v2 = v2 - v1
	local angle = math.atan2(v2.y, v2.x)

	while angle > 2 * math.pi do
		angle = angle - 2 * math.pi
	end
	while angle < 0 do
		angle = angle + 2 * math.pi
	end

	if (bone.fFixedAngle) then
		bone.fAnimAngle:SetValue(0, angle)
	else
		local angleDiff = angle - bone.fAnimAngle:GetValue(0)
		bone.fAnimAngle:SetValue(0, angle)
		for keyID = 0, bone.fAnimAngle:CountKeys() - 1 do
			local angleFrame = bone.fAnimAngle:GetKeyWhen(keyID)
			if (angleFrame > 0) then
				local newAngle = bone.fAnimAngle:GetValue(angleFrame) + angleDiff
				bone.fAnimAngle:SetValue(angleFrame, newAngle)
			end
		end

		for actionID = 0, bone.fAnimAngle:CountActions() - 1 do
			local action = moho:ChannelAsAnimVal(bone.fAnimAngle:Action(actionID))
			for keyID = 0, action:CountKeys() - 1 do
				local angleFrame = action:GetKeyWhen(keyID)
				if (angleFrame > 0) then
					local newAngle = action:GetValue(angleFrame) + angleDiff
					action:SetValue(angleFrame, newAngle)
				end
			end
		end
	end

	if (bone.fAnimParent:CountKeys() < 2) then
		bone.fAnimParent:SetValue(0, bone.fParent)
	end
	bone.fParent = parentBoneID
	bone.fAnimParent:SetValue(parentFrame, parentBoneID)
end

function MR_TransformRigTool:TranslateLayer(moho, layer, offset)
	local localOffset = LM.Vector2:new_local()
	local zeroOffset = LM.Vector2:new_local()
	localOffset:Set(offset)
	if layer:Parent() then
		localOffset:Set(self:GetLocalPos(moho, layer:Parent(), localOffset))
		zeroOffset:Set(self:GetLocalPos(moho, layer:Parent(), zeroOffset))
		localOffset:Set(localOffset - zeroOffset)
	end
	local vec = LM.Vector3:new_local()
	vec:Set(layer.fTranslation.value)
	vec.x = vec.x + localOffset.x
	vec.y = vec.y + localOffset.y
	layer.fTranslation:SetValue(0, vec)
	
	for actionID = 0, layer.fTranslation:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec3(layer.fTranslation:Action(actionID))
		local actionName = layer.fTranslation:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local newPos = LM.Vector3:new_local()
					local layerTranslation = action:GetValue(actionFrame)
					
					newPos:Set(layerTranslation.x + localOffset.x, layerTranslation.y + localOffset.y, layerTranslation.z)
					action:SetValue(actionFrame, newPos)
					if layerTranslation ~= newPos then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end	
	end
end

function MR_TransformRigTool:ScaleLayer(moho, layer, center, scaling)
	local curOrigin = LM.Vector2:new_local()
	local posDif = LM.Vector3:new_local()
	local curPos = LM.Vector3:new_local()
	curPos:Set(layer.fTranslation:GetValue(0))
	curOrigin:Set(layer:Origin())
	local centerVec = LM.Vector2:new_local()
	centerVec = self:GetLocalPos(moho, layer, center) -- use global pos
	self:SetOrigin(moho, layer, centerVec)
	local vec = LM.Vector3:new_local()
	vec:Set(layer.fScale.value)
	if self.scalingMode == 1 then
		vec.x = vec.x * scaling.x
		vec.y = vec.y * scaling.x
		vec.z = vec.z * scaling.x
	elseif self.scalingMode == 2 then
		vec.x = vec.x * scaling.x
	elseif self.scalingMode == 3 then
		vec.y = vec.y * scaling.y
	end
	layer.fScale:SetValue(0, vec)
	
	self:SetOrigin(moho, layer, curOrigin)
	posDif:Set(layer.fTranslation:GetValue(0) - curPos)
	
	for actionID = 0, layer.fTranslation:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec3(layer.fTranslation:Action(actionID))
		local actionName = layer.fTranslation:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local newPos = LM.Vector3:new_local()
					local layerTranslation = action:GetValue(actionFrame)
					newPos:Set(layerTranslation + posDif)
					action:SetValue(actionFrame, newPos)
					if layerTranslation ~= newPos then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end	
	end
	for actionID = 0, layer.fScale:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec3(layer.fScale:Action(actionID))
		local actionName = layer.fScale:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local newScale = LM.Vector3:new_local()
					local layerScale = action:GetValue(actionFrame)
					if self.scalingMode == 1 then -- uniform
						newScale:Set(layerScale * scaling.x)
					elseif self.scalingMode == 2 then -- h
						newScale:Set(layerScale.x * scaling.x, layerScale.y, layerScale.z)
					elseif self.scalingMode == 3 then -- v
						newScale:Set(layerScale.x, layerScale.y  * scaling.y, layerScale.z)
					end	
					action:SetValue(actionFrame, newScale)
					if layerScale ~= newScale then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end	
	end
end

function MR_TransformRigTool:RotateLayer(moho, layer, center, angle)
	local curOrigin = LM.Vector2:new_local()
	curOrigin:Set(layer:Origin())
	
	local curPos = LM.Vector3:new_local()
	local posDif = LM.Vector3:new_local()
	curPos:Set(layer.fTranslation:GetValue(0))
	
	local centerVec = LM.Vector2:new_local()
	centerVec = self:GetLocalPos(moho, layer, center) -- use global pos
	self:SetOrigin(moho, layer, centerVec)
	local layerAngle = layer.fRotationZ:GetValue(0)
	local localAngle = angle
	if self:CheckLayerParentalFlip(moho, layer, true) then
		localAngle = -localAngle
	end
	layerAngle = layerAngle + localAngle
	layer.fRotationZ:SetValue(0, layerAngle)
	self:SetOrigin(moho, layer, curOrigin)
	posDif:Set(layer.fTranslation:GetValue(0) - curPos)
	
	for actionID = 0, layer.fTranslation:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec3(layer.fTranslation:Action(actionID))
		local actionName = layer.fTranslation:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local newPos = LM.Vector3:new_local()
					local layerTranslation = action:GetValue(actionFrame)
					newPos:Set(layerTranslation + posDif)
					
					action:SetValue(actionFrame, newPos)
					if layerTranslation ~= newPos then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end	
	end
	for actionID = 0, layer.fRotationZ:CountActions() - 1 do
		local action = moho:ChannelAsAnimVal(layer.fRotationZ:Action(actionID))
		local actionName = layer.fRotationZ:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local actionFrame = action:GetKeyWhen(keyID)
				if (actionFrame > 0) then
					local layerAngle = action:GetValue(actionFrame)
					local newAngle = layerAngle + localAngle
					action:SetValue(actionFrame, newAngle)
					if layerAngle ~= newAngle then
						self:CollectLog(nil, actionName)
					end	
				end
			end
		end	
	end
end

function MR_TransformRigTool:FlipLayer(moho, layer, center, direction, lastFlip)
	local curOrigin = LM.Vector2:new_local()
	local localCenter = LM.Vector2:new_local()
	localCenter:Set(center)
	curOrigin:Set(layer:Origin())
	local skelMatrix = LM.Matrix:new_local()
	if lastFlip and not self.useGlobalFlip then
		if self.lastSkelLayer ~= nil then
			self.lastSkelLayer:GetFullTransform(0, skelMatrix, nil)
		end
	end		
	localCenter = self:GetLocalPos(moho, layer, center) -- use global pos
	self:SetOrigin(moho, layer, localCenter)
	if direction then
		layer.fFlipH:SetValue(0, not layer.fFlipH.value)
	else
		layer.fFlipV:SetValue(0, not layer.fFlipV.value)
	end
	local parenOffset = 0
	if layer:Parent() then
		local parentLayerMatrix = LM.Matrix:new_local()
		local parentLayer = layer:Parent()
		parentLayer:GetFullTransform(0, parentLayerMatrix, nil)
		if lastFlip and not self.useGlobalFlip then
			
		elseif not self.useGlobalFlip then
			self.skelLayer:GetFullTransform(0, skelMatrix, nil)
		end
		
		local v1 = LM.Vector2:new_local()
		local v2 = LM.Vector2:new_local()
		v1:Set(parentLayer:Origin())
		v2:Set(v1.x - 0.1, v1.y)
		
		parentLayerMatrix:Transform(v1)
		parentLayerMatrix:Transform(v2)
		
		if not self.useGlobalFlip then
			skelMatrix:Invert()
			skelMatrix:Transform(v1)
			skelMatrix:Transform(v2)
		end	
		
		if direction then
			v2:Set(v1.x + (v1.x - v2.x),v2.y)
		else
			v2:Set(v2.x, v1.y + (v1.y - v2.y))
		end
		local newAngle = 0
		v2 = v2 - v1
		newAngle = math.atan2(v2.y, v2.x)
		while newAngle > math.rad(360) do
			newAngle = newAngle - math.rad(360)
		end
		while newAngle < - math.rad(360) do
			newAngle = newAngle + math.rad(360)
		end	
		parenOffset = newAngle
	end

	if self:CheckLayerParentalFlip(moho, layer, false) then
		parenOffset = -(parenOffset * 2)
	else	
		parenOffset = parenOffset * 2
	end
	layer.fRotationZ:SetValue(0, -(layer.fRotationZ:GetValue(0)) + parenOffset)
	self:SetOrigin(moho, layer, curOrigin)
end

function MR_TransformRigTool:CorrectScalingOffsetInActions(moho, bone, center, scaling)
	local skel = self.skel
	for actionID = 0, bone.fAnimPos:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
		local actionName = bone.fAnimPos:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local posFrame = action:GetKeyWhen(keyID)
				if (posFrame > 0) then
					local startBonePos = LM.Vector2:new_local()
					local originalBonePos = LM.Vector2:new_local()
					local centerVec = LM.Vector2:new_local()
					local newPos = LM.Vector2:new_local()
					local dif = LM.Vector2:new_local()
					startBonePos:Set(action:GetValue(posFrame))
					originalBonePos:Set(startBonePos)
					if bone.fParent < 0 then
						centerVec:Set(center)
						centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
						dif:Set(startBonePos - centerVec)
						newPos:Set((dif * scaling.x) + centerVec)
					else
						self.skelLayer:ActivateAction(actionName)
						moho:SetCurFrame(posFrame)
						local parentBoneMatrix = LM.Matrix:new_local()
						local parentBone = skel:Bone(bone.fParent)
						parentBoneMatrix = parentBone.fMovedMatrix
						parentBoneMatrix:Transform(startBonePos)
						centerVec:Set(center)
						centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
						dif:Set(startBonePos - centerVec)
						parentBoneMatrix:Invert()
						newPos:Set((dif * scaling.x) + centerVec)
						parentBoneMatrix:Transform(newPos)
					end
					action:SetValue(posFrame, newPos)
					if originalBonePos ~= newPos then
						self:CollectLog(nil, actionName)
					end
					self.skelLayer:ActivateAction(nil)
				end		
			end			
		end
	end
	moho:SetCurFrame(0)
end	

function MR_TransformRigTool:CorrectRotatingOffsetInActions(moho, bone, center, angle)
	local skel = self.skel
	for actionID = 0, bone.fAnimPos:CountActions() - 1 do
		local action = moho:ChannelAsAnimVec2(bone.fAnimPos:Action(actionID))
		local actionName = bone.fAnimPos:ActionName(actionID)
		if action ~= nil then
			for keyID = 0, action:CountKeys() - 1 do
				local posFrame = action:GetKeyWhen(keyID)
				if (posFrame > 0) then
					local startBonePos = LM.Vector2:new_local()
					local originalBonePos = LM.Vector2:new_local()
					local centerVec = LM.Vector2:new_local()
					local newPos = LM.Vector2:new_local()
					local dif = LM.Vector2:new_local()
					startBonePos:Set(action:GetValue(posFrame))
					originalBonePos:Set(startBonePos)
					centerVec:Set(center)
					centerVec:Set(self:GetLocalPos(moho, self.skelLayer, centerVec))
					if bone.fParent < 0 then
						dif:Set(startBonePos - centerVec)
						local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
						local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
						newPos:Set(px + centerVec.x, py + centerVec.y)
					else
						self.skelLayer:ActivateAction(actionName)
						moho:SetCurFrame(posFrame)
						local parentBoneMatrix = LM.Matrix:new_local()
						local parentBone = skel:Bone(bone.fParent)
						parentBoneMatrix = parentBone.fMovedMatrix
						parentBoneMatrix:Transform(startBonePos)
						dif:Set(startBonePos - centerVec)
						local px = dif.x * math.cos(angle) - dif.y * math.sin(angle)
						local py = dif.x * math.sin(angle) + dif.y * math.cos(angle)
						newPos:Set(px + centerVec.x, py + centerVec.y)
						parentBoneMatrix:Invert()
						parentBoneMatrix:Transform(newPos)
					end
					action:SetValue(posFrame, newPos)
					if originalBonePos ~= newPos then
						self:CollectLog(nil, actionName)
					end	
					self.skelLayer:ActivateAction(nil)
				end
			end
		end
	end	
	moho:SetCurFrame(0)
end

function MR_TransformRigTool:RefreshCachedLayers(moho)
	for k, id in ipairs(self.vectorLayersToChange.layer) do
		local layer = moho.document:LayerByAbsoluteID(id)
		layer:FreeCachedImage()
	end	
end

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

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

	phrase['Description'] = 'Transformation, duplication and copying body parts. Ctrl+Alt+Click for setting Custom Center.'
	phrase['UILabel'] = 'Transform Rig Tool'

	phrase['Advanced Mode'] = 'Advanced Mode'
	phrase['Finalize'] = 'Apply and leave advanced mode'
	phrase['Clear'] = 'Cancel and leave advanced mode'
	
    phrase['Settings'] = 'Settings'
	phrase['Live transformation settings:'] = 'Live transformation settings:'
	phrase['Show Transform Info'] = 'Show transformation info'
	phrase['Use Global Flip'] = 'Use global flip'
	phrase['Adaptive scale and rotation'] = 'Adaptive scale and rotation'
	phrase['Use Line Width'] = 'Use Line Width'
	phrase['Live updating references'] = 'Live updating references'
	phrase['Duplicate bodypart settings:'] = 'Duplicate bodypart settings:'
	phrase['Duplicated names suffix'] = 'Names suffix'
	phrase['Advaned mode settings:'] = 'Advaned mode settings:'
	phrase['Consider new parent rotation'] = 'Consider new parent rotation'
    phrase['Consider old parent rotation'] = 'Consider old parent rotation'
	
	phrase['Custom Center'] = 'Custom Center'
	phrase['Selected Bone'] = 'Selected Bone'
	phrase['Set From Selection'] = 'Set to selection center (Alt+Click to set to the selected bone position)'
	
	phrase['Duplicate Bodypart'] = 'Duplicate Bodypart'
	phrase['Copy Bodypart'] = 'Copy Bodypart'
	phrase['Flip Horizontally'] = 'Flip Horizontally (Alt+Click to copy the previous flip)'
	phrase['Flip Vertically'] = 'Flip Vertically (Alt+Click to copy the previous flip)'
	phrase['Duplicate And Flip Smarbone'] = 'Duplicate Smarbone (Alt+Click to duplicate and flip)'
	phrase['Reconnect Bones'] = 'Preserve Bones Parenting (Alt+Click to unlink, then Click to relink)'
	phrase['Select All Points'] = 'Select All Points (Alt+Click - deselect points)'
	phrase['Check For Split Dimensions'] = 'Check For Split Dimensions'
	
	phrase['Ignore References'] = 'Ignore Reference Layers'
	phrase['Transform Points'] = 'Transform Points'
	phrase['Auto Select All Points'] = 'Auto Select All Points'
	phrase['Adjust Stroke Width'] = 'Adjust Stroke Width'
	phrase['Follow Path Rig Adaptation'] = 'Follow Path Rig Adaptation'
	phrase['Transform Bones'] = 'Transform Bones'
	phrase['Transform Vitruvian Bones'] = 'Transform Vitruvian Bones'
	phrase['Transform Target Bones'] = 'Transform Target Bones'
	phrase['Transform Patch Layers'] = 'Transform Patch Layers'
	phrase['Transform Image Layers'] = 'Transform Image Layers'
	phrase['Transform Origins'] = 'Transform Layer Origin'
	
	phrase['Select Bone'] = 'Select Bone'
	phrase['Translate'] = 'Translate'
	phrase['Rotate'] = 'Rotate'
	phrase['Scale'] = 'Scale'
	
	phrase['Update Center'] = 'Update Center For Repeat Last Transformation (Alt+Click to set the center to layer\'s origin)'
	phrase['Repeat Transformation'] = 'Repeat Last Transformation'
	phrase['Repeat Translate'] = 'Repeat Last Translation. (Alt+Click to translate in the opposite direction)'
	phrase['Repeat Rotate'] = 'Repeat Last Rotation. (Alt+Click to rotate in the opposite direction)'
	phrase['Repeat Scale'] = 'Repeat Last Scaling. (Alt+Click to scale in the opposite direction)'
	phrase['Edit Last Transform'] = 'Edit'
	phrase['X'] = 'X'
    phrase['Y'] = 'Y'
	phrase['Angle'] = 'A'
	phrase['Apply'] = 'Apply'
	
	phrase['Collected data cleared.'] = 'Advanced mode info cleared.'
	phrase['Canceled.'] = 'Canceled.'
	phrase['Original transformation collected for 1 bone on layer '] = 'Collected info for 1 bone on layer '
	phrase['Original transformation collected for '] = 'Collected info for '
	phrase[' bones on layer '] = ' bones in layer '
	phrase['No bones was selected. Please select bones first.'] = 'No bones were selected. Please select some bones first.'
	phrase['Correction was canceled.'] = 'Operation was canceled.'
	phrase['Correction completed! '] = 'Completed! '
	phrase[' fixed in '] = ' updated in '
	phrase[' actions.'] = ' actions.'
	phrase[' bones were'] = ' bones were'
	phrase[' bone was'] = ' bone was'
	phrase[' action.'] = ' action.'
	phrase[' actions were updated.'] = ' actions were updated.'
	phrase[' action was updated.'] = ' action was updated.'
	phrase['Could not find any actions that need to be fixed.'] = 'No actions to update.'
	phrase['Actions did not need to be updated'] = 'No updates for actions needed.'
	phrase['Could not find a Rig.'] = 'Could not find a Rig.'
	phrase['You need to select one bone in this mode.'] = 'You need to select one bone in this mode.'
	phrase['Only one bone should be selected.'] = 'Only one bone should be selected.'
	phrase['No layers selected for active mode.'] = 'No layers selected for active mode.'
	phrase['Non uniform scaling not supported for bones.'] = 'Non-uniform scaling is not supported for bones.'
	
	phrase['Channels with separated dimensions were found.'] = 'Channels with separated dimensions were found.'
	phrase['You can Rejoin Dimensions to continue or cancel operation.'] = 'You can Rejoin Dimensions to continue or cancel operation.'
	phrase['Rejoin Dimensions'] = 'Rejoin Dimensions'
	phrase['Cancel'] = 'Cancel'
	phrase['Some bones are missing. The skeleton structure was changed.'] = 'Some bones are missing. The skeleton structure was changed.'
	phrase['EXIT'] = 'EXIT'
	phrase['Some reference keys are missing. Please do not remove or edit any reference keys.'] = 'Some reference keys are missing. Please do not remove or edit any reference keys.'
	phrase['The original number of bones is different.'] = 'The original number of bones is different.'
	phrase['Is smartbone is right?'] = 'Is smartbone is right?'
	phrase['If smartbone is right all R sufixes will be renamed to L'] = 'If smartbone is right all R sufixes will be renamed to L'
	phrase['Right'] = 'Right'
	phrase['Left'] = 'Left'
	phrase['Enter New Name'] = 'Enter New Name'
	phrase['Duplicate bodypart alert 1'] = 'Please select one bone and some nested layers.'
	phrase['Duplicate bodypart alert 2'] = 'The skeleton layer must not be selected.'
	phrase['Copy bodypart alert 1'] = '1. Select target skeleton.'
	phrase['Copy bodypart alert 2'] = '2. Select layers in the source skeleton and a bone.'
	phrase['Do You want to rejoin dimensions?'] = 'Would you like to rejoin dimensions?'
	phrase['No channels with separated dimensions were found.'] = 'No channels with separated dimensions were found.'
	phrase['No last flip to repeat'] = 'There is no previous flip to repeat.'
	
	phrase['As Action'] = '*'
	phrase['As Smartbone'] = '+'
	phrase['Remove'] = '-'
	phrase['Set R'] = '➞ R'
	phrase['Set L'] = '➞ L'
	phrase['Check Names'] = '        Check Names        '
	phrase['Reset Names'] = '        Reset Names        '
	phrase['Auto Mode'] = '  Suggest  '
	phrase['Duplicate only action'] = 'Duplicate only action'
	phrase['Duplicate smartbone'] = 'Duplicate action and smartbone'
	phrase['Do not duplicate action'] = 'Do not duplicate action'
	phrase['Smartbone name does not changed'] = 'Name is not changed'
	phrase['Smartbone name is changed'] = 'Name is changed'
	phrase['This smartbone name is used'] = 'This name is already used'
	phrase['This smartbone name is not used'] = 'This name is not used'
	phrase['Smartbones Manager'] = 'Smartbones Manager'
	
	phrase['Missing Utilites'] = 'AE_Utilities is not installed.'
	phrase['Missing Utilites Info'] = 'AE_Utilities.lua is required for this tool. Please install the utilities file.'
	
	phrase['Please chose how to rename bones and actions:'] = 'Please chose how to rename bones and actions:'
	phrase['Direction Mode'] = 'Side Mode'
	phrase['Custom Mode'] = 'Replacement Mode'
	phrase['Suffix Mode'] = 'Add Suffix'
	phrase['Replace Text'] = 'Replace Text'
	phrase['Counter delimiter'] = 'Counter delimiter'
	phrase['Please chose how to rename smartbone:'] = 'Please chose how to rename smartbone:'
	phrase['Rename Smartbone'] = 'Enter New Name'
	phrase['Duplicate smartbone alert 1'] = 'Please select a single smartbone'
	phrase['Apply Custom Translate'] = 'Apply Custom Translation. (Alt+Click to translate in the opposite direction)'
	phrase['Apply Custom Scale'] = 'Apply Custom Scaling. (Alt+Click to scale in the opposite direction)'
	phrase['Apply Custom Rotation'] = 'Apply Custom Rotation. (Alt+Click to rotate in the opposite direction)'
	
	phrase['Suggest Tooltip'] = 'Apply suggested settings (Alt+Click to reset)'
	phrase['Duplicate Actions Tooltip'] = 'Set all to Duplicate Only Action'
	phrase['Remove Actions Tooltip'] = 'Set all to Do Not Duplicate Action'
	phrase['Replace Text 1 Tooltip'] = 'Text to find'
	phrase['Replace Text 2 Tooltip'] = 'Text to replace with'
	phrase['Replace Text Button Tooltip'] = 'Find and replace in all action names (Alt+Click to swap)'
	phrase['Check Names Tooltip'] = 'Check if names are available'
	phrase['Reset Names Tooltip'] = 'Revert all names back to originals'
	
	phrase['Direction Mode Tooltip'] = 'The names will be changed based on their side'
	phrase['R to L Tooltip'] = 'Replace all R with L (Alt+Click to swap)'
	phrase['L to R Tooltip'] = 'Replace all L with R (Alt+Click to swap)'
	phrase['Custom Mode Tooltip'] = 'The names will be changed by Find And Replace'
	phrase['Reverse Tooltip'] = 'Swap Find and Replace'
	phrase['Suffix Mode Tooltip'] = 'The suffix will be added for duplicated names'
	phrase['Suffix Tooltip'] = 'The suffix to add'
	phrase['Rename Smartbone Tooltip'] = 'Rename the smartbone and its corresponding action'
	phrase['Swap Mode'] = 'Swap Text'
	phrase['Swap Mode Tooltip'] = 'Swap text instead of replacing'
	
	local fileWord = MOHO.Localize("/Menus/File/File=File")
	if fileWord == "Файл" then
		phrase['Description'] = 'Трансфомация, дублирование и перенос частей рига. Ctrl+Alt+Click для назначения Свободного Центра.'
		phrase['UILabel'] = 'Transform Rig Tool'

		phrase['Advanced Mode'] = 'Продвинутый режим'
		phrase['Finalize'] = 'Применить и покинуть продвинутый режим'
		phrase['Clear'] = 'Отменить и покинуть продвинутый режим'
		
		phrase['Settings'] = 'Настройки'
		phrase['Live transformation settings:'] = 'Трансформация в реальном времени:'
		phrase['Show Transform Info'] = 'Показывать значения при трансформации'
		phrase['Use Global Flip'] = 'Глобальное отражение'
		phrase['Adaptive scale and rotation'] = 'Адаптивные масштаб и вращение'
		phrase['Use Line Width'] = 'Изменять толщину точек для контура'
		phrase['Live updating references'] = 'Обновлеять референсы в реальном времени'
		phrase['Duplicate bodypart settings:'] = 'Настройки дублирования частей рига:'
		phrase['Duplicated names suffix'] = 'Суффикс для занятых имён'
		phrase['Advaned mode settings:'] = 'Настройки продвинутого режима:'
		phrase['Consider new parent rotation'] = 'Учитывать вращение нового родителя'
		phrase['Consider old parent rotation'] = 'Учитывать вращение старого родителя'
		
		phrase['Custom Center'] = 'Свободный Центр'
		phrase['Selected Bone'] = 'Выделенная кость'
		phrase['Set From Selection'] = 'Установить в центр выделенного обьекта (Alt+Click - установить в основание выделенной кости)'
		
		phrase['Duplicate Bodypart'] = 'Дублировать часть рига'
		phrase['Copy Bodypart'] = 'Скопировать часть рига в другой скелет'
		phrase['Flip Horizontally'] = 'Отразить горизонтально (Alt+Click - Повторить с учётом предыдущего отражения)'
		phrase['Flip Vertically'] = 'Отразить вертикально (Alt+Click - Повторить с учётом предыдущего отражения)'
		phrase['Duplicate And Flip Smarbone'] = 'Дублировать смартбоун (Alt+Click - дублировать и отразить)'
		phrase['Reconnect Bones'] = 'Запомнить дочерние связи костей. (Alt+Click - отвязать, Click - привязать обратно)'
		phrase['Select All Points'] = 'Выделить все точки (Alt+Click - снять выделение)'
		phrase['Check For Split Dimensions'] = 'Проверить наличие разделённых каналов'
		
		phrase['Ignore References'] = 'Игнорировать слои-референсы'
		phrase['Transform Points'] = 'Трансформировать точки'
		phrase['Auto Select All Points'] = 'Авто-выделение точек'
		phrase['Adjust Stroke Width'] = 'Изменять ширину контура под масштаб'
		phrase['Follow Path Rig Adaptation'] = 'Деформировать под риги с движением по кривой'
		phrase['Transform Bones'] = 'Трансформировать кости'
		phrase['Transform Vitruvian Bones'] = 'Трансформировать Витрувианские кости'
		phrase['Transform Target Bones'] = 'Трансформировать целевые кости'
		phrase['Transform Patch Layers'] = 'Трансформировать слои-заплатки'
		phrase['Transform Image Layers'] = 'Трансформировать растровые слои'
		phrase['Transform Origins'] = 'Трансформировать центр слоя'
		
		phrase['Select Bone'] = 'Выделить кость'
		phrase['Translate'] = 'Перемещать'
		phrase['Rotate'] = 'Вращать'
		phrase['Scale'] = 'Масштабировать'
		
		phrase['Update Center'] = 'Обновить центр для повторение трансформации (Alt+Click - назначить центр из центра слоя)'
		phrase['Repeat Transformation'] = 'Повторить последнюю трансформацию'
		phrase['Repeat Translate'] = 'Повторить последние перемещение. (Alt+Click - в обратном направлении)'
		phrase['Repeat Rotate'] = 'Повторить последнее вращение. (Alt+Click - в обратном направлении)'
		phrase['Repeat Scale'] = 'Повторить последнее масштабирование. (Alt+Click - в обратном направлении)'
		phrase['Edit Last Transform'] = 'Редактировать'
		phrase['X'] = 'X'
		phrase['Y'] = 'Y'
		phrase['Angle'] = 'A'
		phrase['Apply'] = 'Применить'
		
		phrase['Collected data cleared.'] = 'Информация продвинутого режима очищена.'
		phrase['Canceled.'] = 'Отменено.'
		phrase['Original transformation collected for 1 bone on layer '] = 'Проанализирована 1 кость в слое '
		phrase['Original transformation collected for '] = 'Проанализированы '
		phrase[' bones on layer '] = ' кости(-ей) в слое '
		phrase['No bones was selected. Please select bones first.'] = 'Нет выделенных костей. Выделите какие-нибудь кости.'
		phrase['Correction was canceled.'] = 'Операция отменена.'
		phrase['Correction completed! '] = 'Завершено! '
		phrase[' fixed in '] = ' обновлено в '
		phrase[' actions.'] = ' действиях.'
		phrase[' bones were'] = ' кости были'
		phrase[' bone was'] = ' кость была'
		phrase[' action.'] = ' действие.'
		phrase[' actions were updated.'] = ' действий(-ия) были обновлены.'
		phrase[' action was updated.'] = ' действие обновлено.'
		phrase['Could not find any actions that need to be fixed.'] = 'Нет действий для обновления.'
		phrase['Actions did not need to be updated'] = 'Действия в обновлениях не нуждаются.'
		phrase['Could not find a Rig.'] = 'Риг не найден.'
		phrase['You need to select one bone in this mode.'] = 'Выделите только 1 кость в этом режиме.'
		phrase['Only one bone should be selected.'] = 'Только 1 кость должна быть выделина.'
		phrase['No layers selected for active mode.'] = 'Нет выделенных слоёв для этого режима.'
		phrase['Non uniform scaling not supported for bones.'] = 'Неравномерное масштабирование не работает с костями.'
		
		phrase['Channels with separated dimensions were found.'] = 'Найдены разделённые каналы.'
		phrase['You can Rejoin Dimensions to continue or cancel operation.'] = 'Объедините каналы или отмените операцию.'
		phrase['Rejoin Dimensions'] = 'Объединить каналы'
		phrase['Cancel'] = 'Отмена'
		phrase['Some bones are missing. The skeleton structure was changed.'] = 'Некоторые кости отсутствуют. Структура скелета была изменена.'
		phrase['EXIT'] = 'Выход'
		phrase['Some reference keys are missing. Please do not remove or edit any reference keys.'] = 'Некоторые рефенсные ключи отсутствуют. Не удаляйте и не редактируйте их.'
		phrase['The original number of bones is different.'] = 'Изначальное количество костей отличается.'
		phrase['Is smartbone is right?'] = 'Is smartbone is right?'
		phrase['If smartbone is right all R sufixes will be renamed to L'] = 'If smartbone is right all R sufixes will be renamed to L'
		phrase['Right'] = 'Right'
		phrase['Left'] = 'Left'
		phrase['Enter New Name'] = 'Enter New Name'
		phrase['Duplicate bodypart alert 1'] = 'Выделите одну кость и слои внутри скелета.'
		phrase['Duplicate bodypart alert 2'] = 'Сам слой скелета не должен быть выделенным.'
		phrase['Copy bodypart alert 1'] = '1. Выделите скелет КУДА копировать.'
		phrase['Copy bodypart alert 2'] = '2. Выделите слои в скелете ОТКУДА копировать и одну кость.'
		phrase['Do You want to rejoin dimensions?'] = 'Соединить каналы?'
		phrase['No channels with separated dimensions were found.'] = 'Разъединённые каналы не найдены.'
		phrase['No last flip to repeat'] = 'Ничего не отражалось, чтобы повторить.'
		
		phrase['As Action'] = '*'
		phrase['As Smartbone'] = '+'
		phrase['Remove'] = '-'
		phrase['Set R'] = '➞ R'
		phrase['Set L'] = '➞ L'
		phrase['Check Names'] = '     Проверить имена      '
		phrase['Reset Names'] = '      Сбросить имена     '
		phrase['Auto Mode'] = 'Рекомендовать'
		phrase['Duplicate only action'] = 'Дублировать только действие'
		phrase['Duplicate smartbone'] = 'Дублировать действие и смартбоун'
		phrase['Do not duplicate action'] = 'Не дублировать действие'
		phrase['Smartbone name does not changed'] = 'Имя не изменено'
		phrase['Smartbone name is changed'] = 'Имя изменено'
		phrase['This smartbone name is used'] = 'Это имя занято'
		phrase['This smartbone name is not used'] = 'Это имя свободно'
		phrase['Smartbones Manager'] = 'Менеджер смартбоунов'
		
		phrase['Missing Utilites'] = 'AE_Utilities не установлены.'
		phrase['Missing Utilites Info'] = 'AE_Utilities.lua необходим для этого инструмента. Пожалуйста, установите этот файл.'
		
		phrase['Please chose how to rename bones and actions:'] = 'Выберите способ переименования костей и действий:'
		phrase['Direction Mode'] = 'Режим сторон'
		phrase['Custom Mode'] = 'Режим замены'
		phrase['Suffix Mode'] = 'Добавить суффикс'
		phrase['Replace Text'] = 'Заменить текст'
		phrase['Counter delimiter'] = 'Разделитель счетчика'
		phrase['Please chose how to rename smartbone:'] = 'Выберите способ переименования смартбоуна:'
		phrase['Rename Smartbone'] = 'Новое имя'
		phrase['Duplicate smartbone alert 1'] = 'Выделите один смартбоун'
		phrase['Apply Custom Translate'] = 'Применить пользовательское смещение. (Alt+Click - в обратном направлении)'
		phrase['Apply Custom Scale'] = 'Применить пользовательское масштабирование. (Alt+Click - в обратном направлении)'
		phrase['Apply Custom Rotation'] = 'Применить пользовательское вращение. (Alt+Click - в обратном направлении)'
		
		phrase['Suggest Tooltip'] = 'Применить рекомендованые настройки (Alt+Click - сбросить)'
		phrase['Duplicate Actions Tooltip'] = 'Включить всем \'Дублировать только действие\''
		phrase['Remove Actions Tooltip'] = 'Включить всем \'Не дублировать действие\''
		phrase['Replace Text 1 Tooltip'] = 'Что найти'
		phrase['Replace Text 2 Tooltip'] = 'На что заменить'
		phrase['Replace Text Button Tooltip'] = 'Найти и заменить во всех именах действий (Alt+Click - поменять местами)'
		phrase['Check Names Tooltip'] = 'Проверить, не заняты ли имена'
		phrase['Reset Names Tooltip'] = 'Вернуть все имена в исходный вид'
		
		phrase['Direction Mode Tooltip'] = 'Имена будут изменены основываясь на стороне'
		phrase['R to L Tooltip'] = 'Заменить все R на L (Alt+Click - поменять местами)'
		phrase['L to R Tooltip'] = 'Заменить все L на R (Alt+Click - поменять местами)'
		phrase['Custom Mode Tooltip'] = 'Имена будут изменены путём поиска и замены'
		phrase['Reverse Tooltip'] = 'Поменять местами строки поиска и замены'
		phrase['Suffix Mode Tooltip'] = 'Добавить суффикс к совпадающим именам'
		phrase['Suffix Tooltip'] = 'Текст суффикса'
		phrase['Rename Smartbone Tooltip'] = 'Переименовать смартбоун и его действие'
		phrase['New Smartbone Name Tooltip'] = 'Новое имя для смартбоуна'
		phrase['Swap Mode'] = 'Менять местами текст'
		phrase['Swap Mode Tooltip'] = 'Менять местами текст вместо замены'
	end
	return phrase[text]
end

Icon
MR Transform Rig Tool
Listed

Script type: Tool

Uploaded: May 30 2022, 06:38

Last modified: Jun 01 2022, 14:57

This tool allows you to transform parts of the rig without breaking its actions.
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: 196