-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "AE_BoneMagnet" -- ************************************************** -- General information about this script -- ************************************************** AE_BoneMagnet = {} function AE_BoneMagnet:Name() return 'Bone Magnet' end function AE_BoneMagnet:Version() return '1.1' end function AE_BoneMagnet:UILabel() return 'Bone Magnet' end function AE_BoneMagnet:Creator() return 'Alexandra Evseeva' end function AE_BoneMagnet:Description() return '' end function AE_BoneMagnet:ColorizeIcon() return true end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function AE_BoneMagnet:IsRelevant(moho) if moho:CountBones() > 0 then return true end return false end function AE_BoneMagnet:IsEnabled(moho) if moho.frame == 0 then return false end local skel = moho:Skeleton() if (skel == nil) then return false end return true end -- ************************************************** -- Recurring values -- ************************************************** AE_BoneMagnet.newMethod = false AE_BoneMagnet.magnetRadius = 0.5 AE_BoneMagnet.selectedOnly = true AE_BoneMagnet.dragging = false AE_BoneMagnet.dragVec = LM.Vector2:new_local() AE_BoneMagnet.dragVec:Set(10000.0, 10000.0) AE_BoneMagnet.dragResize = false AE_BoneMagnet.selectionMode = false AE_BoneMagnet.startRadius = 0 AE_BoneMagnet.minVec = LM.Vector2:new_local() AE_BoneMagnet.maxVec = LM.Vector2:new_local() AE_BoneMagnet.fixChildren = true function AE_BoneMagnet:LoadPrefs(prefs) self.magnetRadius = prefs:GetFloat("AE_BoneMagnet.magnetRadius", 0.5) self.selectedOnly = prefs:GetBool("AE_BoneMagnet.selectedOnly", false) self.fixChildren = prefs:GetBool("AE_BoneMagnet.fixChildren", true) end function AE_BoneMagnet:SavePrefs(prefs) prefs:SetFloat("AE_BoneMagnet.magnetRadius", self.magnetRadius) prefs:SetBool("AE_BoneMagnet.selectedOnly", self.selectedOnly) prefs:SetBool("AE_BoneMagnet.fixChildren", self.fixChildren) end function AE_BoneMagnet:ResetPrefs() self.magnetRadius = 0.5 self.selectedOnly = false self.fixChildren = true end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function AE_BoneMagnet:NonDragMouseMove() return true -- Call MouseMoved() even if the mouse button is not down end function AE_BoneMagnet:OnInputDeviceEvent(moho, deviceEvent) return LM_TransformBone:OnInputDeviceEvent(moho, deviceEvent) end function AE_BoneMagnet:OnMouseDown(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end if (mouseEvent.altKey) then self.dragResize = true self.startRadius = self.magnetRadius self.dragVec:Set(mouseEvent.drawingVec) mouseEvent.view:DrawMe() return elseif (mouseEvent.ctrlKey) then self.selectionMode = true mouseEvent.ctrlKey = false LM_SelectBone:OnMouseDown(moho, mouseEvent) return end self.dragging = true self.dragVec:Set(mouseEvent.drawingVec) moho.document:PrepUndo(moho.drawingLayer) moho.document:SetDirty() ------------------------------------ --prepare an array to move--------------- --look at LM_Magnet same method structure (lines 119-158) local frame = moho.layerFrame local actionName = moho.document:CurrentDocAction() self.boneList = {} for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) local drawBone = self:IsBonePickable(moho, skel, bone) if drawBone then local v1 = LM.Vector2:new_local() local v2 = LM.Vector2:new_local() v1:Set(0,0) v2:Set(bone.fLength, 0) local boneInfo = {id=i, points={{start=v1},{start=v2}}} for i, point in pairs(boneInfo.points) do if (moho.frame == 0) then bone.fRestMatrix:Transform(point.start) else bone.fMovedMatrix:Transform(point.start) end local v = point.start - mouseEvent.drawingVec local magInfluence = v:Mag() if self.newMethod then magInfluence = magInfluence * magInfluence magInfluence = 0.5 / (1 + magInfluence * magInfluence * 1000) if (magInfluence <= 0.001) then magInfluence = 0 end else if magInfluence <= self.magnetRadius then magInfluence = magInfluence / self.magnetRadius if (magInfluence > 1.0) then magInfluence = 1.0 end magInfluence = LM.Slerp(magInfluence, 1, 0) else magInfluence = 0 end end point.influence = magInfluence point.current = LM.Vector2:new_local() point.current:Set(point.start) end if boneInfo.points[1].influence > 0 or boneInfo.points[2].influence > 0 then local pos, angle, scale = AE_Utilities:GetGlobalBonePRS(moho, skel, bone, frame, actionName) boneInfo.startAngle = angle boneInfo.startPos = pos boneInfo.startScale = scale boneInfo.startMatrix = LM.Matrix:new_local() boneInfo.startMatrix:Set(bone.fMovedMatrix) if frame == 0 then boneInfo.startMatrix:Set(bone.fRestMatrix) end table.insert(self.boneList, boneInfo) end end end ----------------------------------------- mouseEvent.view:DrawMe() end function AE_BoneMagnet:OnMouseMoved(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end if (self.dragResize) then self.magnetRadius = self.startRadius + (mouseEvent.pt.x - mouseEvent.startPt.x) / 600 if (self.magnetRadius < 0.01) then self.magnetRadius = 0.01 end if (self.magnetRadius > 5.0) then self.magnetRadius = 5.0 end mouseEvent.view:DrawMe() return elseif (self.selectionMode) then mouseEvent.ctrlKey = false LM_SelectBone:OnMouseMoved(moho, mouseEvent) return end self.dragVec:Set(mouseEvent.drawingVec) if (not self.dragging) then mouseEvent.view:DrawMe() return end local offset = mouseEvent.drawingVec - mouseEvent.drawingStartVec --TODO:---------------------------------- --apply motion to saved array items (self.selList or something like this)--------------- --use lines 194-196 of LM_Magnet (but not very useful) for i, boneInfo in pairs(self.boneList) do --local bone = skel:Bone(boneInfo.id) for j, point in pairs(boneInfo.points) do point.current = point.start + offset * point.influence end end ----------------------------------------- mouseEvent.view:DrawMe() end function AE_BoneMagnet:OnMouseUp(moho, mouseEvent) local skel = moho:Skeleton() if (skel == nil) then return end self.dragging = false self.dragVec:Set(mouseEvent.drawingVec) if (self.dragResize) then self.dragResize = false mouseEvent.view:DrawMe() return elseif (self.selectionMode) then mouseEvent.ctrlKey = false LM_SelectBone:OnMouseUp(moho, mouseEvent) self.selectionMode = false mouseEvent.view:DrawMe() return end ------------------------------------ --apply postions from array to bones --use lines 223-228 of LM_Magnet (but not very useful) self.boneCheckList = {} for i, boneInfo in pairs(self.boneList) do self.boneCheckList[boneInfo.id] = boneInfo end for i=0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fParent == -1 then self:ApplyToBone(moho,skel,bone,i) end end ----------------------------------------- moho.layer:UpdateCurFrame() moho:UpdateSelectedChannels() moho:UpdateUI() self.boneList = nil self.boneCheckList = nil end function AE_BoneMagnet:CropAngle(angle) if math.abs(angle) < math.pi then return angle end local new_angle = (math.abs(angle) % (2*math.pi)) * angle/math.abs(angle) if math.abs(new_angle) > math.pi then local pi = 2 * math.pi * new_angle/math.abs(new_angle) new_angle = new_angle - pi end return new_angle end function AE_BoneMagnet:ApplyToBone(moho, skel, bone, id) local boneInfo = self.boneCheckList[id] local frame = moho.layerFrame local actionName = moho.document:CurrentDocAction() if boneInfo then --apply position, angle and scale in moho.frame local pos, angle, scale = AE_Utilities:GetGlobalBonePRS(moho, skel, bone, frame, actionName, moho.layer) local globalPos = LM.Vector2:new_local() globalPos:Set(boneInfo.points[1].current) if bone.fParent > -1 then --move globalPos to parent space local parentBone = skel:Bone(bone.fParent) local matrix = AE_Utilities:GetGlobalBoneMatrix(moho, skel, parentBone, frame, actionName) matrix:Invert() matrix:Transform(globalPos) matrix:Transform(pos) end local posDif = globalPos - pos if posDif:Mag() > 0.0001 then bone.fAnimPos:SetValue(frame, bone.fAnimPos:GetValue(frame) + posDif) end if bone.fLength > 0 then local vector = boneInfo.points[2].current - boneInfo.points[1].current --get desired angle local desiredAngle = math.pi/2 - math.atan2(vector.x, vector.y) local angleDif = desiredAngle - angle angleDif = (math.abs(angleDif)%(2*math.pi))*angleDif/math.abs(angleDif) if math.abs(angleDif) > 0.0001 then angleDif = self:CropAngle(angleDif) bone.fAnimAngle:SetValue(frame, bone.fAnimAngle:GetValue(frame) + angleDif) boneInfo.globalAngleDif = desiredAngle - boneInfo.startAngle end --get desired scale if frame == 0 then boneInfo.lengthAdd = vector:Mag() - bone.fLength bone.fLength = vector:Mag() else local desiredScale = vector:Mag()/bone.fLength local scaleDif = desiredScale - scale if math.abs(scaleDif) > 0.001 then bone.fAnimScale:SetValue(frame, bone.fAnimScale:GetValue(frame) + scaleDif) end end elseif bone.fParent > -1 and self.boneCheckList[bone.fParent] then local parentInfo = self.boneCheckList[bone.fParent] local parentBone = skel:Bone(bone.fParent) local parentAngleDif = parentInfo.globalAngleDif if parentAngleDif and not bone.fFixedAngle then parentAngleDif = self:CropAngle(parentAngleDif) bone.fAnimAngle:SetValue(frame, bone.fAnimAngle:GetValue(frame) - parentAngleDif) end end elseif bone.fParent > -1 and self.boneCheckList[bone.fParent] and self:IsBonePickable(moho, skel, bone) then if self.fixChildren then -- fix angle to old global (rotate inverse parent dif) local parentInfo = self.boneCheckList[bone.fParent] local parentBone = skel:Bone(bone.fParent) local parentAngleDif = parentInfo.globalAngleDif if parentAngleDif and not bone.fFixedAngle then parentAngleDif = self:CropAngle(parentAngleDif) bone.fAnimAngle:SetValue(frame, bone.fAnimAngle:GetValue(frame) - parentAngleDif) end --also fix child position calculating it from parent PRS change local oldPos = bone.fAnimPos:GetValue(frame) parentInfo.startMatrix:Transform(oldPos) local newMatrix = LM.Matrix:new_local() newMatrix = AE_Utilities:GetGlobalBoneMatrix(moho, skel, parentBone, frame, actionName) newMatrix:Invert() newMatrix:Transform(oldPos) bone.fAnimPos:SetValue(frame, oldPos) end end skel:UpdateBoneMatrix(id) for i=0, skel:CountBones()-1 do local childBone = skel:Bone(i) if childBone.fParent == id then self:ApplyToBone(moho, skel, childBone, i) end end end function AE_BoneMagnet:OnKeyDown(moho, keyEvent) if (not self.dragging) then LM_SelectBone:OnKeyDown(moho, keyEvent) end end function AE_BoneMagnet:IsBonePickable(moho, skel, bone) if bone.fHidden then return false end if self.selectedOnly and not bone.fSelected then return false end if (bone.fAngleControlParent >= 0 or bone.fPosControlParent >= 0 or bone.fScaleControlParent >= 0 or bone.fBoneDynamics.value ) then return false end return true end function AE_BoneMagnet:DrawMe(moho, view) local skel = moho:Skeleton() if (skel == nil) then return end if (self.selectionMode) then LM_SelectBone:DrawMe(moho, view) return end local vc1 = LM.ColorVector:new_local() local vc2 = LM.ColorVector:new_local() vc1:Set(MOHO.MohoGlobals.SelCol) vc2:Set(MOHO.MohoGlobals.BackCol) --vc1 = (vc1 * 3 + vc2 * 4) / 7 vc2 = (vc1 + vc2) / 2 local normOutlineCol = vc1:AsColorStruct() local normFillCol = vc2:AsColorStruct() local markerR = 6 local v = LM.Vector2:new_local() local v2 = LM.Vector2:new_local() local g = view:Graphics() local layerMatrix = LM.Matrix:new_local() moho.layer:GetFullTransform(moho.frame, layerMatrix, moho.document) g:Push() g:ApplyMatrix(layerMatrix) g:SetSmoothing(true) g:SetBezierTolerance(2) local outlineCol = normOutlineCol local fillCol = normFillCol g:SetPenWidth(1) --TODO:---------------------------------- if (not self.dragging) then --draw something like sketchbones do for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) local drawBone = self:IsBonePickable(moho, skel, bone) if drawBone then v:Set(0, 0) if (moho.frame == 0) then bone.fRestMatrix:Transform(v) else bone.fMovedMatrix:Transform(v) end if bone.fLength > 0 then v2:Set(bone.fLength, 0) if (moho.frame == 0) then bone.fRestMatrix:Transform(v2) else bone.fMovedMatrix:Transform(v2) end g:SetColor(outlineCol) g:DrawLine(v.x, v.y, v2.x, v2.y) g:FrameCirclePixelRadius(v2, markerR) g:SetColor(fillCol) g:FillCirclePixelRadius(v2, markerR) end g:SetColor(outlineCol) g:FrameCirclePixelRadius(v, markerR) g:SetColor(fillCol) g:FillCirclePixelRadius(v, markerR) end end else --draw same stucture, but from array, not real bones for i, boneInfo in pairs(self.boneList) do local bone = skel:Bone(boneInfo.id) local v = boneInfo.points[1].current local v2 = boneInfo.points[2].current if bone.fLength > 0 then g:SetColor(outlineCol) g:DrawLine(v.x, v.y, v2.x, v2.y) g:FrameCirclePixelRadius(v2, markerR) g:SetColor(fillCol) g:FillCirclePixelRadius(v2, markerR) end g:SetColor(outlineCol) g:FrameCirclePixelRadius(v, markerR) g:SetColor(fillCol) g:FillCirclePixelRadius(v, markerR) end end ----------------------------------------- g:Pop() MOHO.DrawToolSizeCircle(moho, view, self.dragVec, self.magnetRadius, self.dragResize) end -- ************************************************** -- Tool options - create and respond to tool's UI -- ************************************************** AE_BoneMagnet.CHANGE = MOHO.MSG_BASE AE_BoneMagnet.DUMMY = MOHO.MSG_BASE + 1 AE_BoneMagnet.RESET = MOHO.MSG_BASE + 2 AE_BoneMagnet.SELECTITEM = MOHO.MSG_BASE + 3 function AE_BoneMagnet:DoLayout(moho, layout) layout:AddChild(LM.GUI.StaticText("Magnet radius")) self.radius = LM.GUI.TextControl(0, "00.0000", self.CHANGE, LM.GUI.FIELD_UFLOAT) self.radius:SetWheelInc(0.01) layout:AddChild(self.radius) self.selectedCheck = LM.GUI.CheckBox("Selected bones only", self.CHANGE) layout:AddChild(self.selectedCheck) self.fixChildrenCheck = LM.GUI.CheckBox("Fix Children", self.CHANGE) layout:AddChild(self.fixChildrenCheck) --layout:AddChild(LM.GUI.Button(MOHO.Localize("/Scripts/Tool/Magnet/Reset=Reset"), self.RESET)) end function AE_BoneMagnet:UpdateWidgets(moho) local skel = moho:Skeleton() if (skel == nil) then return end self.radius:SetValue(self.magnetRadius) self.selectedCheck:SetValue(self.selectedOnly) self.fixChildrenCheck:SetValue(self.fixChildren) end function AE_BoneMagnet:HandleMessage(moho, view, msg) local skel = moho:Skeleton() if (skel == nil) then return end if (msg == self.RESET) then self.magnetRadius = 0.5 moho:UpdateUI() elseif (msg == self.CHANGE) then self.magnetRadius = self.radius:FloatValue() if (self.magnetRadius < 0.001) then self.magnetRadius = 0.001 end self.selectedOnly = self.selectedCheck:Value() self.fixChildren = self.fixChildrenCheck:Value() elseif (msg >= self.SELECTITEM) then mesh:SelectNone() local i = msg - self.SELECTITEM local name = mesh:Group(i):Name() mesh:SelectGroup(name) moho:UpdateUI() end end
Bone Magnet
Listed
Author: A.Evseeva
View Script
Script type: Tool
Uploaded: Apr 17 2022, 14:24
Last modified: Apr 22 2022, 12:48
Script Version: 1.1
Just like point magnet, but for bones.
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: 1374
Bone Magnet
Listed
Author: A.Evseeva
View Script
Script type: Tool
Uploaded: Apr 17 2022, 14:24
Last modified: Apr 22 2022, 12:48
Script Version: 1.1
Just like point magnet, but for bones.
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: 1374