-- ************************************************** -- Provide Moho with the name of this script object -- ************************************************** ScriptName = "MR_Path" -- ************************************************** -- General information about this script -- ************************************************** MR_Path = {} function MR_Path:Name() return self:Localize('UILabel') end function MR_Path:Version() return '1.1' end function MR_Path:UILabel() return self:Localize('UILabel') end function MR_Path:Creator() return 'Eugene Babich' end function MR_Path:Description() return self:Localize('Description') end -- ************************************************** -- Is Relevant / Is Enabled -- ************************************************** function MR_Path:IsRelevant(moho) return true end function MR_Path:IsEnabled(moho) return true end -- ************************************************** -- Recurring Values -- ************************************************** MR_Path.pathOpacity = 100 MR_Path.width = 1 MR_Path.pathColorR = 84 MR_Path.pathColorG = 212 MR_Path.pathColorB = 196 MR_Path.pathColorA = 255 MR_Path.centerLine = false MR_Path.drawTimingMark = true MR_Path.timingMarksSize = 0.5 MR_Path.trackBoneTip = false MR_Path.adjacentFramesRange = false MR_Path.adjacentFrames = 10 MR_Path.playbackRange = true MR_Path.fromToRange = false MR_Path.from = 1 MR_Path.to = 20 MR_Path.interval = 1 MR_Path.maxTargets = 60 MR_Path.isMouseDragging = false MR_Path.selRect = LM.Rect:new_local() MR_Path.trackingList = {} MR_Path.trackingList.target = {} MR_Path.trackingList.targetType = {} MR_Path.trackingList.targetID = {} MR_Path.trackingList.targetName = {} MR_Path.trackingList.layerName = {} MR_Path.trackingList.layerUUID = {} MR_Path.trackingList.layer = {} MR_Path.trackingList.boneTip = {} MR_Path.trackingList.color = {} MR_Path.trackingList.description = {} MR_Path.trackingList.pos = {} -- ************************************************** -- Prefs -- ************************************************** function MR_Path:LoadPrefs(prefs) self.pathOpacity = prefs:GetInt("MR_Path.pathOpacity", 100) self.width = prefs:GetFloat("MR_Path.width", 1) self.pathColorR = prefs:GetInt("MR_Path.pathColorR", 84) self.pathColorG = prefs:GetInt("MR_Path.pathColorG", 212) self.pathColorB = prefs:GetInt("MR_Path.pathColorB", 196) self.pathColorB = prefs:GetInt("MR_Path.pathColorA", 255) self.centerLine = prefs:GetBool("MR_Path.centerLine", false) self.drawTimingMark = prefs:GetBool("MR_Path.drawTimingMark", true) self.timingMarksSize = prefs:GetFloat("MR_Path.timingMarksSize", 0.5) self.trackBoneTip = prefs:GetBool("MR_Path.trackBoneTip", false) self.adjacentFramesRange = prefs:GetBool("MR_Path.adjacentFramesRange", false) self.adjacentFrames = prefs:GetInt("MR_Path.adjacentFrames", 10) self.playbackRange = prefs:GetBool("MR_Path.playbackRange", true) self.fromToRange = prefs:GetBool("MR_Path.fromToRange", false) self.from = prefs:GetInt("MR_Path.from", 1) self.to = prefs:GetInt("MR_Path.to", 20) self.interval = prefs:GetInt("MR_Path.interval", 1) end function MR_Path:SavePrefs(prefs) prefs:SetInt("MR_Path.pathOpacity", self.pathOpacity) prefs:SetFloat("MR_Path.width", self.width) prefs:SetInt("MR_Path.pathColorR", self.pathColorR) prefs:SetInt("MR_Path.pathColorG", self.pathColorG) prefs:SetInt("MR_Path.pathColorB", self.pathColorB) prefs:SetInt("MR_Path.pathColorA", self.pathColorA) prefs:SetBool("MR_Path.centerLine", self.centerLine) prefs:SetBool("MR_Path.drawTimingMark", self.drawTimingMark) prefs:SetFloat("MR_Path.timingMarksSize", self.timingMarksSize) prefs:SetBool("MR_Path.trackBoneTip", self.trackBoneTip) prefs:SetBool("MR_Path.adjacentFramesRange", self.adjacentFramesRange) prefs:SetInt("MR_Path.adjacentFrames", self.adjacentFrames) prefs:SetBool("MR_Path.playbackRange", self.playbackRange) prefs:SetBool("MR_Path.fromToRange", self.fromToRange) prefs:SetInt("MR_Path.from", self.from) prefs:SetInt("MR_Path.to", self.to) prefs:SetInt("MR_Path.interval", self.interval) end function MR_Path:ResetPrefs() self.pathOpacity = 100 self.width = 1 self.pathColorR = 84 self.pathColorG = 212 self.pathColorB = 196 self.pathColorA = 255 self.centerLine = false self.drawTimingMark = true self.timingMarksSize = 0.5 self.trackBoneTip = false self.adjacentFramesRange = false self.adjacentFrames = 10 self.playbackRange = true self.fromToRange = false self.from = 1 self.to = 20 self.interval = 1 end -- ************************************************** -- Keyboard/Mouse Control -- ************************************************** function MR_Path:OnMouseDown(moho, mouseEvent) local skel = moho:Skeleton() local mesh = moho:Mesh() if not skel and not mesh then return end self.isMouseDragging = true if not mouseEvent.shiftKey and not mouseEvent.altKey then if skel then skel:SelectNone() elseif mesh then mesh:SelectNone() end end self.selRect.left = mouseEvent.startPt.x self.selRect.top = mouseEvent.startPt.y self.selRect.right = mouseEvent.pt.x self.selRect.bottom = mouseEvent.pt.y mouseEvent.view:Graphics():SelectionRect(self.selRect) mouseEvent.view:DrawMe() end function MR_Path:OnMouseMoved(moho, mouseEvent) local skel = moho:Skeleton() local mesh = moho:Mesh() if not skel and not mesh then return end mouseEvent.view:Graphics():SelectionRect(self.selRect) self.selRect.right = mouseEvent.pt.x self.selRect.bottom = mouseEvent.pt.y mouseEvent.view:Graphics():SelectionRect(self.selRect) mouseEvent.view:RefreshView() mouseEvent.view:DrawMe() end function MR_Path:OnMouseUp(moho, mouseEvent) local skel = moho:Skeleton() local mesh = moho:Mesh() local mouseDist = math.abs(mouseEvent.pt.x - mouseEvent.startPt.x) + math.abs(mouseEvent.pt.y - mouseEvent.startPt.y) local id if skel then id = mouseEvent.view:PickBone(mouseEvent.pt, mouseEvent.vec, moho.layer, true) if not mouseEvent.shiftKey and not mouseEvent.altKey then skel:SelectNone() end if id ~= -1 then skel:Bone(id).fSelected = not mouseEvent.altKey end elseif mesh then id = mouseEvent.view:PickPoint(mouseEvent.pt) if not mouseEvent.shiftKey and not mouseEvent.altKey then mesh:SelectNone() end if id ~= -1 then mesh:Point(id).fSelected = not mouseEvent.altKey end end self.isMouseDragging = false local v = LM.Vector2:new_local() local screenPt = LM.Point:new_local() local m = LM.Matrix:new_local() if moho.layer:LayerType() == MOHO.LT_BONE then if (skel ~= nil) then self.selRect:Normalize() moho.layer:GetFullTransform(moho.frame, m, moho.document) for i = 0, skel:CountBones() - 1 do local bone = skel:Bone(i) local boneMatrix = bone.fMovedMatrix for j = 0, 10 do v:Set(bone.fLength * j / 10.0, 0) boneMatrix:Transform(v) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, screenPt) if (self.selRect:Contains(screenPt)) then if (mouseEvent.altKey) then bone.fSelected = false else bone.fSelected = true end break end end end end elseif moho.layer:LayerType() == MOHO.LT_VECTOR then if (mesh ~= nil) then for i = 0, mesh:CountPoints() - 1 do local v = LM.Vector2:new_local() local screenPt = LM.Point:new_local() local m = LM.Matrix:new_local() self.selRect:Normalize() moho.drawingLayer:GetFullTransform(moho.frame, m, moho.document) for i = 0, mesh:CountPoints() - 1 do local pt = mesh:Point(i) if (not pt.fHidden) then v:Set(pt.fPos) m:Transform(v) mouseEvent.view:Graphics():WorldToScreen(v, screenPt) if (self.selRect:Contains(screenPt)) then if (mouseEvent.altKey) then pt.fSelected = false else pt.fSelected = true end end end end self.isMouseDragging = false end end end moho:UpdateSelectedChannels() end function MR_Path:DrawMe(moho, view) if self.isMouseDragging then local g = view:Graphics() g:SelectionRect(self.selRect) end end local MR_PathManagerDialog = {} MR_PathManagerDialog.dynamicNumberText = {} MR_PathManagerDialog.resetButton = {} MR_PathManagerDialog.dynamicSymbolText = {} MR_PathManagerDialog.targetType = {} MR_PathManagerDialog.targetID = {} MR_PathManagerDialog.layer = {} MR_PathManagerDialog.boneTip = {} MR_PathManagerDialog.color = {} MR_PathManagerDialog.removeCheck = {} MR_PathManagerDialog.editedStatus = {} MR_PathManagerDialog.edited = '*' MR_PathManagerDialog.notEdited = ' ' MR_PathManagerDialog.CHANGE = MOHO.MSG_BASE MR_PathManagerDialog.RANDOMIZE_COLORS = MOHO.MSG_BASE + 1 MR_PathManagerDialog.RESET_SETTINGS = MOHO.MSG_BASE + 2 MR_PathManagerDialog.COPY_SETTINGS = MOHO.MSG_BASE + MR_Path.maxTargets + 2 function MR_PathManagerDialog:new(moho) local d = LM.GUI.SimpleDialog(MR_Path:Localize('UILabel'), MR_PathManagerDialog) local l = d:GetLayout() d.dynamicHeaderText = LM.GUI.DynamicText(MR_Path:Localize('Path Target Manager'), 0) l:AddChild(d.dynamicHeaderText, LM.GUI.ALIGN_CENTER, 0) l:AddChild(LM.GUI.Divider(false), LM.GUI.ALIGN_FILL) local listLength = #MR_Path.trackingList.targetID if not MR_Path.trackingList.targetID[1] then d.dynamicText = LM.GUI.DynamicText(MR_Path:Localize('Path target list is empty'), 0) l:AddChild(d.dynamicText, LM.GUI.ALIGN_LEFT, 0) else l:PushH() for i = 1, listLength do d.editedStatus[i] = d.notEdited 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 <= MR_Path.maxTargets 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] d.dynamicSymbolText[i] = LM.GUI.DynamicText(symbolText, 0) d.dynamicSymbolText[i]:SetToolTip(MR_Path:Localize('Settings not changed')) l:AddChild(d.dynamicSymbolText[i], LM.GUI.ALIGN_LEFT, 0) d.resetButton[i] = LM.GUI.Button('R', self.RESET_SETTINGS + i) d.resetButton[i]:SetToolTip(MR_Path:Localize('Reset settings')) d.resetButton[i]:SetAlternateMessage(self.COPY_SETTINGS + i) l:AddChild(d.resetButton[i], LM.GUI.ALIGN_LEFT, 0) local typeText = MR_Path.trackingList.targetType[i] local layerNameText = ' Layer: \"'.. MR_Path.trackingList.layer[i]:Name()..'\"' local targetID = MR_Path.trackingList.targetID[i] local targetIDText = targetID targetIDText = ' ID: '.. targetIDText d.targetType[i] = LM.GUI.DynamicText(typeText..targetIDText, 0) d.targetType[i]:SetToolTip(MR_Path.trackingList.description[i]..layerNameText) l:AddChild(d.targetType[i], LM.GUI.ALIGN_LEFT, 0) if targetID < 10 then l:AddPadding(14) elseif targetID < 100 then l:AddPadding(8) elseif targetID < 1000 then l:AddPadding(2) else l:AddPadding(-4) end d.color[i] = LM.GUI.ShortColorSwatch(true, self.CHANGE) d.color[i]:SetToolTip(MR_Path:Localize('Path Color Tooltip')) l:AddChild(d.color[i], LM.GUI.ALIGN_LEFT) if MR_Path.trackingList.targetType[i] == "Point" then d.boneTip[i] = LM.GUI.CheckBox(MR_Path:Localize(''), d.CHANGE) d.boneTip[i]:SetToolTip(MR_Path:Localize('Track bone tip')) l:AddChild(d.boneTip[i], LM.GUI.ALIGN_LEFT, 0) d.boneTip[i]:Enable(false) elseif MR_Path.trackingList.targetType[i] == "Bone" then d.boneTip[i] = LM.GUI.CheckBox(MR_Path:Localize(''), d.CHANGE) d.boneTip[i]:SetToolTip(MR_Path:Localize('Track bone tip')) l:AddChild(d.boneTip[i], LM.GUI.ALIGN_LEFT, 0) end l:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) l:AddPadding(1) d.removeCheck[i] = LM.GUI.CheckBox(MR_Path:Localize(''), d.CHANGE) d.removeCheck[i]:SetToolTip(MR_Path:Localize('Remove from tracking')) l:AddChild(d.removeCheck[i], LM.GUI.ALIGN_LEFT, 0) l:Pop() if i == listLength and i ~= 16 and i ~= 31 and i ~= 46 and i ~= 61 then l:Pop() end end end l:Pop() d.randomizeColorsButton = LM.GUI.Button(MR_Path:Localize('Randomize colors'), self.RANDOMIZE_COLORS) l:AddChild(d.randomizeColorsButton, LM.GUI.ALIGN_RIGHT, 0) end return d end function MR_PathManagerDialog:UpdateWidgets(moho) for i = 1, #MR_Path.trackingList.targetID do self.color[i]:SetValue(MR_Path.trackingList.color[i]) self.boneTip[i]:SetValue(MR_Path.trackingList.boneTip[i]) end end function MR_PathManagerDialog:HandleMessage(msg) local targetListLength = #MR_Path.trackingList.targetID if msg == self.CHANGE then self:CheckTargetStatus(moho) elseif msg == self.RANDOMIZE_COLORS then for i = 1, targetListLength do local pathColor = LM.rgb_color:new_local() pathColor.r = math.random(0, 255) pathColor.g = math.random(0, 255) pathColor.b = math.random(0, 255) pathColor.a = 255 self.color[i]:SetValue(pathColor) end self:CheckTargetStatus(moho) elseif msg >= self.RESET_SETTINGS and msg <= self.RESET_SETTINGS + targetListLength then local index = msg - self.RESET_SETTINGS self.color[index]:SetValue(MR_Path.trackingList.color[index]) self.boneTip[index]:SetValue(MR_Path.trackingList.boneTip[index]) self.removeCheck[index]:SetValue(false) self:CheckTargetStatus(moho) elseif msg >= self.COPY_SETTINGS and msg <= self.COPY_SETTINGS + targetListLength then local index = msg - self.COPY_SETTINGS for i = 1, targetListLength do self.color[i]:SetValue(self.color[index]:Value()) if MR_Path.trackingList.targetType[i] == 'Bone' then self.boneTip[i]:SetValue(self.boneTip[index]:Value()) end self.removeCheck[i]:SetValue(self.removeCheck[index]:Value()) end self:CheckTargetStatus(moho) end end function MR_PathManagerDialog:CheckTargetStatus(moho) for i = 1, #MR_Path.trackingList.targetID do local isSettingsChanged = false local color = self.color[i]:Value() if color.r ~= MR_Path.trackingList.color[i].r or color.g ~= MR_Path.trackingList.color[i].g or color.b ~= MR_Path.trackingList.color[i].b or color.a ~= MR_Path.trackingList.color[i].a then isSettingsChanged = true end if self.boneTip[i]:Value() ~= MR_Path.trackingList.boneTip[i] then isSettingsChanged = true end if isSettingsChanged then self.editedStatus[i] = self.edited self.dynamicSymbolText[i]:SetValue(self.editedStatus[i]) self.dynamicSymbolText[i]:SetToolTip(MR_Path:Localize('Settings changed')) else self.editedStatus[i] = self.notEdited self.dynamicSymbolText[i]:SetValue(self.editedStatus[i]) self.dynamicSymbolText[i]:SetToolTip(MR_Path:Localize('Settings not changed')) end end end function MR_PathManagerDialog:OnOK(moho) for i = 1, #MR_Path.trackingList.targetID do local colorSwatchValue = self.color[i]:Value() self.pathColorR = colorSwatchValue.r self.pathColorG = colorSwatchValue.g self.pathColorB = colorSwatchValue.b self.pathColorA = colorSwatchValue.a MR_Path.trackingList.color[i] = colorSwatchValue MR_Path.trackingList.boneTip[i] = self.boneTip[i]:Value() MR_Path.removeList[i] = self.removeCheck[i]:Value() end end -- ************************************************** -- Tool Panel Layout -- ************************************************** MR_Path.ADD_TO_TRACKING = MOHO.MSG_BASE MR_Path.REMOVE_FROM_TRACKING = MOHO.MSG_BASE + 1 MR_Path.CLEAR_TRECKING_LIST = MOHO.MSG_BASE + 2 MR_Path.CREATE_PATH = MOHO.MSG_BASE + 3 MR_Path.CLEAR_PATH = MOHO.MSG_BASE + 4 MR_Path.DELETE_PATH_LAYER = MOHO.MSG_BASE + 5 MR_Path.SHOW_HIDE_PATH = MOHO.MSG_BASE + 6 MR_Path.PATH_OPACITY = MOHO.MSG_BASE + 7 MR_Path.WIDTH = MOHO.MSG_BASE + 8 MR_Path.COLOR = MOHO.MSG_BASE + 9 MR_Path.DRAW_CENTER_LINE = MOHO.MSG_BASE + 10 MR_Path.DRAW_TIMING_MARK = MOHO.MSG_BASE + 11 MR_Path.TIMING_MARKS_SIZE = MOHO.MSG_BASE + 12 MR_Path.TRACK_BONE_TIP = MOHO.MSG_BASE + 13 MR_Path.ADJACENT_FRAMES_RANGE = MOHO.MSG_BASE + 14 MR_Path.ADJACENT_FRAMES = MOHO.MSG_BASE + 15 MR_Path.PLAYBACK_RANGE = MOHO.MSG_BASE + 16 MR_Path.FROM_TO_RANGE = MOHO.MSG_BASE + 17 MR_Path.FROM = MOHO.MSG_BASE + 18 MR_Path.TO = MOHO.MSG_BASE + 19 MR_Path.INTERVAL_1 = MOHO.MSG_BASE + 20 MR_Path.INTERVAL_2 = MOHO.MSG_BASE + 21 MR_Path.PATH_TARGET_MANAGER = MOHO.MSG_BASE + 22 function MR_Path:DoLayout(moho, layout) self.addToTrackingButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_add_to_tracking', self:Localize('Add to tracking'), false, self.ADD_TO_TRACKING, false) layout:AddChild(self.addToTrackingButton, LM.GUI.ALIGN_LEFT, 0) self.removeFromTrackingButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_remove_from_tracking', self:Localize('Remove from tracking'), false, self.REMOVE_FROM_TRACKING, false) layout:AddChild(self.removeFromTrackingButton, LM.GUI.ALIGN_LEFT, 0) self.clearTrackingListButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_clear_tracking_list', self:Localize('Clear tracking list'), false, self.CLEAR_TRECKING_LIST, false) layout:AddChild(self.clearTrackingListButton, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.createPathButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_create_path', self:Localize('Create path'), false, self.CREATE_PATH, false) layout:AddChild(self.createPathButton, LM.GUI.ALIGN_LEFT, 0) self.clearPathButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_clear_path', self:Localize('Clear path'), false, self.CLEAR_PATH, false) layout:AddChild(self.clearPathButton, LM.GUI.ALIGN_LEFT, 0) self.deletePathLayerButton = LM.GUI.ImageButton('ScriptResources/mr_path/mr_delete_path_layer', self:Localize('Delete path layer'), false, self.DELETE_PATH_LAYER, false) layout:AddChild(self.deletePathLayerButton, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.showHidePathCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_visibility', self:Localize('Show hide'), true, self.SHOW_HIDE_PATH, false) layout:AddChild(self.showHidePathCheck, LM.GUI.ALIGN_LEFT, 0) self.pathOpacityInput = LM.GUI.TextControl(0, '100', self.PATH_OPACITY, LM.GUI.FIELD_INT, self:Localize('Path Opacity:')) self.pathOpacityInput:SetToolTip(self:Localize('Opacity for all Paths')) layout:AddChild(self.pathOpacityInput, LM.GUI.ALIGN_LEFT, 0) self.widthInput = LM.GUI.TextControl(0, '100', self.WIDTH, LM.GUI.FIELD_UFLOAT, self:Localize('Width:')) self.widthInput:SetWheelInc(1.0) self.widthInput:SetWheelInteger(true) self.widthInput:SetToolTip(self:Localize('Width for all Paths')) layout:AddChild(self.widthInput, LM.GUI.ALIGN_LEFT, 0) self.colorText = LM.GUI.DynamicText(self:Localize('Color'), 0) layout:AddChild(self.colorText, LM.GUI.ALIGN_LEFT, 0) self.pathColorSwatch = LM.GUI.ShortColorSwatch(true, self.COLOR) layout:AddChild(self.pathColorSwatch, LM.GUI.ALIGN_LEFT) self.pathColorSwatch:SetToolTip(MR_Path:Localize('Path Color Tooltip')) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.centerLineCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_center_line', self:Localize('Draw center line'), true, self.DRAW_CENTER_LINE, false) layout:AddChild(self.centerLineCheck, LM.GUI.ALIGN_LEFT, 0) self.drawTimingMarkCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_draw_timing_marks', self:Localize('Draw timing marks'), true, self.DRAW_TIMING_MARK, false) layout:AddChild(self.drawTimingMarkCheck, LM.GUI.ALIGN_LEFT, 0) self.timingMarksSizeInput = LM.GUI.TextControl(0, '100', self.TIMING_MARKS_SIZE, LM.GUI.FIELD_FLOAT, self:Localize('Size:')) self.timingMarksSizeInput:SetWheelInc(0.01) self.timingMarksSizeInput:SetToolTip(self:Localize('Timing marks size')) layout:AddChild(self.timingMarksSizeInput, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.trackBoneTipCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_track_bone_tip', self:Localize('Track bone tip'), true, self.TRACK_BONE_TIP, false) layout:AddChild(self.trackBoneTipCheck, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.adjacentFramesRangeCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_adjacent_frames', self:Localize('Adjacent frames range'), true, self.ADJACENT_FRAMES_RANGE, false) layout:AddChild(self.adjacentFramesRangeCheck, LM.GUI.ALIGN_LEFT, 0) self.adjacentFramesInput = LM.GUI.TextControl(0, '100', self.ADJACENT_FRAMES, LM.GUI.FIELD_INT, '') self.adjacentFramesInput:SetToolTip(self:Localize('Adjacent frames')) layout:AddChild(self.adjacentFramesInput, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.playbackRangeCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_playback_range', self:Localize('Playback range'), true, self.PLAYBACK_RANGE, false) layout:AddChild(self.playbackRangeCheck, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.fromToRangeCheck = LM.GUI.ImageButton('ScriptResources/mr_path/mr_from_to', self:Localize('From to range'), true, self.FROM_TO_RANGE, false) layout:AddChild(self.fromToRangeCheck, LM.GUI.ALIGN_LEFT, 0) self.fromInput = LM.GUI.TextControl(0, '100', self.FROM, LM.GUI.FIELD_INT, '') self.fromInput:SetToolTip(self:Localize('From')) layout:AddChild(self.fromInput, LM.GUI.ALIGN_LEFT, 0) self.toInput = LM.GUI.TextControl(0, '100', self.TO, LM.GUI.FIELD_INT, '') self.toInput:SetToolTip(self:Localize('To')) layout:AddChild(self.toInput, LM.GUI.ALIGN_LEFT, 0) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) layout:AddChild(LM.GUI.StaticText(self:Localize("IntervalText"))) self.intervalMenu = LM.GUI.Menu(MOHO.Localize("Interval=Interval")) self.intervalMenu:AddItem(MOHO.Localize("1=1"), 0, self.INTERVAL_1) self.intervalMenu:AddItemAlphabetically(MOHO.Localize("2=2"), 0, self.INTERVAL_2) self.intervalPopup = LM.GUI.PopupMenu(50, true) self.intervalPopup:SetMenu(self.intervalMenu) layout:AddChild(self.intervalPopup) layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL) self.PathTargetManagerButton = LM.GUI.Button(self:Localize('Path Target Manager'), self.PATH_TARGET_MANAGER) layout:AddChild(self.PathTargetManagerButton, LM.GUI.ALIGN_LEFT, 0) end function MR_Path:UpdateWidgets(moho) local pathLayer, targetLayer = self:CheckExistPathLayer(moho) local isPathLayer = false if pathLayer then isPathLayer = true end local isTarget = false if self.trackingList.targetID[1] then isTarget = true end self.createPathButton:Enable(isTarget) self.clearPathButton:Enable(isPathLayer) self.deletePathLayerButton:Enable(isPathLayer) self.showHidePathCheck:Enable(isPathLayer) if pathLayer then self.showHidePathCheck:SetValue(pathLayer:IsVisible()) pathLayer.fAlpha:GetValue(0) else self.pathOpacityInput:SetValue(self.pathOpacity) self.showHidePathCheck:SetValue(true) end self.widthInput:SetValue(self.width) local pathColor = LM.rgb_color:new_local() pathColor.r = self.pathColorR pathColor.g = self.pathColorG pathColor.b = self.pathColorB pathColor.a = self.pathColorA self.pathColorSwatch:SetValue(pathColor) self.centerLineCheck:SetValue(self.centerLine) self.drawTimingMarkCheck:SetValue(self.drawTimingMark) self.timingMarksSizeInput:SetValue(self.timingMarksSize) self.trackBoneTipCheck:SetValue(self.trackBoneTip) self.adjacentFramesRangeCheck:SetValue(self.adjacentFramesRange) self.adjacentFramesInput:SetValue(self.adjacentFrames) self.adjacentFramesInput:Enable(self.adjacentFramesRange) self.playbackRangeCheck:SetValue(self.playbackRange) self.fromToRangeCheck:SetValue(self.fromToRange) self.fromInput:SetValue(self.from) self.fromInput:Enable(self.fromToRange) self.toInput:SetValue(self.to) self.toInput:Enable(self.fromToRange) self.intervalMenu:SetChecked(self.INTERVAL_1, false) self.intervalMenu:SetChecked(self.INTERVAL_2, false) if (self.interval == 1) then self.intervalMenu:SetChecked(self.INTERVAL_1, true) elseif (self.interval == 2) then self.intervalMenu:SetChecked(self.INTERVAL_2, true) end self.intervalPopup:Redraw() end function MR_Path:HandleMessage(moho, view, msg) local isTarget = false if self.trackingList.targetID[1] then isTarget = true end if msg == self.ADD_TO_TRACKING then self:AddToTracking(moho) self.createPathButton:Enable(isTarget) elseif msg == self.REMOVE_FROM_TRACKING then self:RemoveFromTracking(moho) self.createPathButton:Enable(isTarget) elseif msg == self.CLEAR_TRECKING_LIST then self.trackingList = {} self.trackingList.target = {} self.trackingList.targetType = {} self.trackingList.targetID = {} self.trackingList.targetName = {} self.trackingList.layerName = {} self.trackingList.layerUUID = {} self.trackingList.layer = {} self.trackingList.boneTip = {} self.trackingList.color = {} self.trackingList.description = {} self.trackingList.pos = {} self.createPathButton:Enable(isTarget) elseif msg == self.CREATE_PATH then self:CreatePath(moho) elseif msg == self.CLEAR_PATH then self:ClearPath(moho) elseif msg == self.DELETE_PATH_LAYER then self:CleanUpInvalidTargets(moho) self:DeletePathLayer(moho) elseif msg == self.SHOW_HIDE_PATH then local pathLayer, targetLayer = self:CheckExistPathLayer(moho) if pathLayer then pathLayer:SetVisible(self.showHidePathCheck:Value()) moho:UpdateUI() end elseif msg == self.PATH_OPACITY then self.pathOpacity = LM.Clamp(self.pathOpacityInput:Value(),0 , 100) self.pathOpacityInput:SetValue(self.pathOpacity) local pathLayer, targetLayer = self:CheckExistPathLayer(moho) if pathLayer then moho.document:SetDirty() moho.document:PrepUndo(pathLayer, true) pathLayer.fAlpha:SetValue(0, self.pathOpacity/100) moho:UpdateUI() moho.view:DrawMe() pathLayer:UpdateCurFrame() end elseif msg == self.WIDTH then self.width = LM.Clamp(self.widthInput:Value(), 0.25, 256) self.widthInput:SetValue(self.width) elseif msg == self.COLOR then local colorSwatchValue = self.pathColorSwatch:Value() self.pathColorR = colorSwatchValue.r self.pathColorG = colorSwatchValue.g self.pathColorB = colorSwatchValue.b self.pathColorA = colorSwatchValue.a elseif msg == self.DRAW_CENTER_LINE then self.centerLine = self.centerLineCheck:Value() elseif msg == self.DRAW_TIMING_MARK then self.drawTimingMark = self.drawTimingMarkCheck:Value() elseif msg == self.TIMING_MARKS_SIZE then self.timingMarksSize = LM.Clamp(self.timingMarksSizeInput:Value(), 0.1, 6) self.timingMarksSizeInput:SetValue(self.timingMarksSize) elseif msg == self.TRACK_BONE_TIP then self.trackBoneTip = self.trackBoneTipCheck:Value() elseif msg == self.ADJACENT_FRAMES_RANGE then self.adjacentFramesRange = true self.adjacentFramesRangeCheck:SetValue(true) self.playbackRange = false self.playbackRangeCheck:SetValue(false) self.fromToRange = false self.fromToRangeCheck:SetValue(false) self.adjacentFramesInput:Enable(true) self.fromInput:Enable(false) self.toInput:Enable(false) elseif msg == self.ADJACENT_FRAMES then self.adjacentFrames = LM.Clamp(self.adjacentFramesInput:Value(), 1, 200) self.adjacentFramesInput:SetValue(self.adjacentFrames) elseif msg == self.PLAYBACK_RANGE then self.playbackRange = true self.playbackRangeCheck:SetValue(true) self.adjacentFramesRange = false self.adjacentFramesRangeCheck:SetValue(false) self.fromToRange = false self.fromToRangeCheck:SetValue(false) self.adjacentFramesInput:Enable(false) self.fromInput:Enable(false) self.toInput:Enable(false) elseif msg == self.FROM_TO_RANGE then self.fromToRange = true self.fromToRangeCheck:SetValue(true) self.adjacentFramesRange = false self.adjacentFramesRangeCheck:SetValue(false) self.playbackRange = false self.playbackRangeCheck:SetValue(false) self.adjacentFramesInput:Enable(false) self.fromInput:Enable(true) self.toInput:Enable(true) elseif msg == self.FROM then self.from = LM.Clamp(self.fromInput:Value(), 1, self.toInput:Value() - 1) self.fromInput:SetValue(self.from) elseif msg == self.TO then self.to = LM.Clamp(self.toInput:Value(), self.fromInput:Value() + 1, 2000) self.toInput:SetValue(self.to) elseif (msg >= self.INTERVAL_1 and msg <= self.INTERVAL_2) then local int = 1 if (msg == self.INTERVAL_1) then int = 1 elseif (msg == self.INTERVAL_2) then int = 2 end self.interval = int elseif msg == self.PATH_TARGET_MANAGER then self:BuildDialog(moho) self.createPathButton:Enable(isTarget) end self:UpdateWidgets(moho) end function MR_Path:AddToTracking(moho) local skel = moho:Skeleton() local mesh = moho:Mesh() if not skel and not mesh then return end self:CleanUpInvalidTargets(moho) local pathColor = LM.rgb_color:new_local() pathColor.r = self.pathColorR pathColor.g = self.pathColorG pathColor.b = self.pathColorB pathColor.a = self.pathColorA local outOfLimit = 0 if mesh and moho:CountSelectedPoints() > 0 then for i = 0, mesh:CountPoints()-1 do local point = mesh:Point(i) if point.fSelected then local existInList = self:ValueExists(self.trackingList.target, point) if existInList then self.trackingList.target[existInList] = point self.trackingList.targetType[existInList] = 'Point' self.trackingList.targetID[existInList] = i self.trackingList.targetName[existInList] = i self.trackingList.layerName[existInList] = moho.layer:Name() self.trackingList.layerUUID[existInList] = moho.layer:UUID() self.trackingList.layer[existInList] = moho.layer self.trackingList.boneTip[existInList] = false self.trackingList.color[existInList] = pathColor local skel = moho.layer:ControllingSkeleton() if skel and point.fParent >= 0 then self.trackingList.description[existInList] = 'Parent bone: \"' .. skel:Bone(point.fParent):Name()..'\" ID: '..point.fParent else self.trackingList.description[existInList] = 'No parent bone.' end elseif #self.trackingList.targetID < self.maxTargets then table.insert(self.trackingList.target, point) table.insert(self.trackingList.targetType, 'Point') table.insert(self.trackingList.targetID, i) table.insert(self.trackingList.targetName, i) table.insert(self.trackingList.layerName, moho.layer:Name()) table.insert(self.trackingList.layerUUID, moho.layer:UUID()) table.insert(self.trackingList.layer, moho.layer) table.insert(self.trackingList.boneTip, false) table.insert(self.trackingList.color, pathColor) local skel = moho.layer:ControllingSkeleton() if skel and point.fParent >= 0 then table.insert(self.trackingList.description, 'Parent bone: \"' .. skel:Bone(point.fParent):Name()..'\" ID: '..point.fParent) else table.insert(self.trackingList.description, 'No parent bone.') end local posList = {} posList.vec2 = {} posList.frame = {} table.insert(self.trackingList.pos, posList) else outOfLimit = outOfLimit + 1 end end end end if skel and moho:CountSelectedBones() > 0 then for i = 0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fSelected then local existInList = self:ValueExists(self.trackingList.target, bone) if existInList then self.trackingList.target[existInList] = bone self.trackingList.targetType[existInList] = 'Bone' self.trackingList.targetID[existInList] = i self.trackingList.targetName[existInList] = bone:Name() self.trackingList.layerName[existInList] = moho.layer:Name() self.trackingList.layerUUID[existInList] = moho.layer:UUID() self.trackingList.layer[existInList] = moho.layer self.trackingList.boneTip[existInList] = self.trackBoneTip self.trackingList.color[existInList] = pathColor self.trackingList.description[existInList] = 'Bone: \"' .. bone:Name()..'\"' elseif #self.trackingList.targetID < self.maxTargets then table.insert(self.trackingList.target, bone) table.insert(self.trackingList.targetType, 'Bone') table.insert(self.trackingList.targetID, i) table.insert(self.trackingList.targetName, bone:Name()) table.insert(self.trackingList.layerName, moho.layer:Name()) table.insert(self.trackingList.layerUUID, moho.layer:UUID()) table.insert(self.trackingList.layer, moho.layer) table.insert(self.trackingList.boneTip, self.trackBoneTip) table.insert(self.trackingList.color, pathColor) table.insert(self.trackingList.description, 'Bone: \"' .. bone:Name()..'\"') local posList = {} posList.vec2 = {} posList.frame = {} table.insert(self.trackingList.pos, posList) else outOfLimit = outOfLimit + 1 end end end end if outOfLimit > 0 then local infoAlert = LM.GUI.Alert(LM.GUI.ALERT_INFO, self:Localize('Targets limit exceeded alert 1'), self:Localize('Targets limit exceeded alert 2')..' '..outOfLimit, "", 'OK') end end function MR_Path:RemoveFromTracking(moho) local skel = moho:Skeleton() local mesh = moho:Mesh() if not skel and not mesh then return end self:CleanUpInvalidTargets(moho) if mesh and moho:CountSelectedPoints() > 0 then for i = 0, mesh:CountPoints()-1 do local point = mesh:Point(i) if point.fSelected then local existInList = self:ValueExists(self.trackingList.target, point) if existInList then self:RemoveFromTargetList(moho, existInList) end end end end if skel and moho:CountSelectedBones() > 0 then for i = 0, skel:CountBones()-1 do local bone = skel:Bone(i) if bone.fSelected then local existInList = self:ValueExists(self.trackingList.target, bone) if existInList then self:RemoveFromTargetList(moho, existInList) end end end end end function MR_Path:BuildDialog(moho) self:CleanUpInvalidTargets(moho) self.dlog = MR_PathManagerDialog:new() self.settingsPopup = LM.GUI.PopupDialog(self:Localize('Path Target Manager'), false, 0) self.settingsPopup:SetDialog(self.dlog) self.removeList = {} local dlog = MR_PathManagerDialog:new(moho) if dlog then if (dlog:DoModal() == LM.GUI.MSG_CANCEL) then return false end end local removeListLength = #self.removeList for i = 1, removeListLength do local n = removeListLength + 1 - i if self.removeList[n] then self:RemoveFromTargetList(moho, n) end end end function MR_Path:CleanUpInvalidTargets(moho) local listLength = #self.trackingList.target for i = 1, listLength do local n = listLength + 1 - i if not moho.document:IsLayerValid(self.trackingList.layerUUID[n]) then self:RemoveFromTargetList(moho, n) else if self.trackingList.targetType[n] == 'Point' then local layer = self.trackingList.layer[n] if not moho.document:IsLayerValid(layer) then layer = self:GetLayerByUUID(moho, self.trackingList.layerUUID[n]) if layer then self.trackingList.layer[n] = layer end end if layer then if self.trackingList.target[n]:CountCurves() == 0 then self:RemoveFromTargetList(moho, n) end else self:RemoveFromTargetList(moho, n) end elseif self.trackingList.targetType[n] == 'Bone' then local boneValid = false local layer = self.trackingList.layer[n] if not moho.document:IsLayerValid(layer) then layer = self:GetLayerByUUID(moho, self.trackingList.layerUUID[n]) if layer then self.trackingList.layer[n] = layer end end if layer then local skel = moho:LayerAsBone(layer):Skeleton() if skel then for b=0, skel:CountBones()-1 do local targetBone = skel:Bone(b) if self.trackingList.targetName[n] == targetBone:Name() then self.trackingList.target[n] = targetBone boneValid = true break end end end end if not boneValid then self:RemoveFromTargetList(moho, n) end end end end end function MR_Path:RemoveFromTargetList(moho, n) table.remove(self.trackingList.target, n) table.remove(self.trackingList.targetType, n) table.remove(self.trackingList.targetID, n) table.remove(self.trackingList.targetName, n) table.remove(self.trackingList.layerName, n) table.remove(self.trackingList.layerUUID, n) table.remove(self.trackingList.layer, n) table.remove(self.trackingList.boneTip, n) table.remove(self.trackingList.color, n) table.remove(self.trackingList.description, n) table.remove(self.trackingList.pos, n) end function MR_Path:ValueExists(tbl, value) for key, val in pairs(tbl) do if val == value then return key end end return false end function MR_Path:CreatePath(moho) self:CleanUpInvalidTargets(moho) if not self.trackingList.targetID[1] then return end local curLayer = moho.layer local curFrame = moho.frame local curAction = moho.layer:CurrentAction() local currentQualityFlags = moho.view:QualityFlags() local wireframe = MOHO.hasbit(moho.view:QualityFlags(), MOHO.bit(MOHO.LDQ_WIREFRAME)) if wireframe then moho.view:SetQualityFlags(currentQualityFlags - MOHO.LDQ_WIREFRAME) end local from = 1 local to = 20 if self.adjacentFramesRange then from = curFrame - self.adjacentFrames if from < 1 then from = 1 end to = curFrame + self.adjacentFrames elseif self.playbackRange then from = MOHO.MohoGlobals.PlayStart to = MOHO.MohoGlobals.PlayEnd if from < 1 then from = moho.document:StartFrame() end if to < 2 then to = moho.document:EndFrame() end elseif self.fromToRange then from = self.from to = self.to end if self.interval == 2 then local isCurFrameEven = curFrame % 2 == 0 local isFromFrameEven = from % 2 == 0 local isToFrameEven = to % 2 == 0 if isFromFrameEven ~= isCurFrameEven then if from == 1 then from = from + 1 elseif from > 1 then from = from - 1 end end if isToFrameEven ~= isCurFrameEven then to = to + 1 end end local isSoundtrackMuted = MOHO.MohoGlobals.MuteSoundtrack MOHO.MohoGlobals.MuteSoundtrack = true local targetFrame = from local counter = 1 for i = 1, #self.trackingList.targetID do self.trackingList.pos[i].vec2 = {} self.trackingList.pos[i].frame = {} end repeat moho:SetCurFrame(targetFrame) for i = 1, #self.trackingList.targetID do local pos = LM.Vector2:new_local() local layer = self.trackingList.layer[i] if self.trackingList.targetType[i] == 'Point' then local point = self.trackingList.target[i] pos = self:GetGlobalPos(moho, layer, point.fPos) elseif self.trackingList.targetType[i] == 'Bone' then local bone = self.trackingList.target[i] if self.trackingList.boneTip[i] then if (bone:IsZeroLength()) then pos:Set(0, 0) else pos:Set(bone.fLength, 0) end else pos:Set(0, 0) end bone.fMovedMatrix:Transform(pos) pos = self:GetGlobalPos(moho, layer, pos) end self.trackingList.pos[i].vec2[counter] = pos self.trackingList.pos[i].frame[counter] = targetFrame end counter = counter + 1 targetFrame = targetFrame + self.interval until targetFrame >= to + 1 local pathLayer, targetLayer = self:CheckExistPathLayer(moho) local pathLayerMesh if not pathLayer then moho.document:SetDirty() moho.document:PrepUndo(nil) moho:SetSelLayer(targetLayer) pathLayer = self:CreateNewLayer(moho) pathLayerMesh = moho:LayerAsVector(pathLayer):Mesh() else moho.document:PrepUndo(pathLayer, true) moho.document:SetDirty() pathLayerMesh = moho:LayerAsVector(pathLayer):Mesh() if pathLayerMesh then pathLayerMesh:Clear() pathLayer:SetVisible(true) pathLayer.fAlpha:SetValue(0, self.pathOpacity / 100) end end moho:SetSelLayer(pathLayer) moho:SetCurFrame(0) local pointList = {} pointList.point = {} pointList.color = {} for i = 1, #self.trackingList.targetID do local pointsNumBeforeLines = pathLayerMesh:CountPoints() table.insert(pointList.point, pointsNumBeforeLines) table.insert(pointList.color, self.trackingList.color[i]) local prewPos = LM.Vector2:new_local() for p = 1, #self.trackingList.pos[i].vec2 do local pos = self.trackingList.pos[i].vec2[p] if p == 1 then pathLayerMesh:AddLonePoint(pos, 0) prewPos = self.trackingList.pos[i].vec2[p] else if not self:IsEqual(pos.x, prewPos.x, 0.0001) or not self:IsEqual(pos.y, prewPos.y, 0.0001) then pathLayerMesh:AppendPoint(pos, 0) if p == 2 and i == 1 then local lastPoint = pathLayerMesh:Point(pathLayerMesh:CountPoints()-2) lastPoint.fAnimPos:SetValue(1, lastPoint.fAnimPos:GetValue(0)) end end prewPos = pos end end local totalPoints = pathLayerMesh:CountPoints() for c=0, totalPoints - 1 do local point = pathLayerMesh:Point(c) point:SetCurvature(MOHO.PEAKED, 0) end pathLayerMesh:SelectConnected() local shapeID = moho:CreateShape(false, false, 0) local color = self.trackingList.color[i] if shapeID >= 0 then local shape = pathLayerMesh:Shape(shapeID) local lineWidth = self.width lineWidth = LM.Clamp(lineWidth, 0.25, 256) shape.fMyStyle.fLineWidth = lineWidth / moho.document:Height() shape.fMyStyle.fLineCol:SetValue(0, color) shape.fMyStyle.fLineCaps = 0 shape:MakePlain() end pathLayerMesh:SelectNone() if self.drawTimingMark then local listLength = #self.trackingList.pos[i].vec2 local prewPos = LM.Vector2:new_local() for p = 1, listLength do local frame = self.trackingList.pos[i].frame[p] local pos = self.trackingList.pos[i].vec2[p] local nextPos = LM.Vector2:new_local() local counter = 1 if p == 1 then prewPos = self.trackingList.pos[i].vec2[p] end if p < listLength then local counterNext = 1 repeat nextPos = self.trackingList.pos[i].vec2[p + counterNext] if not self:IsEqual(pos.x, nextPos.x, 0.0001) or not self:IsEqual(pos.y, nextPos.y, 0.0001) then break end counterNext = counterNext + 1 until p + counterNext > listLength else nextPos = self.trackingList.pos[i].vec2[p] end if p == 1 then local notchStart, notchEnd = self:GetTimingMarkPos(moho, nextPos, pos, nextPos, (self.timingMarksSize / 2) * self.width, true, frame) pathLayerMesh:AddLonePoint(notchStart, 0) pathLayerMesh:AppendPoint(notchEnd, 0) else if not self:IsEqual(pos.x, prewPos.x, 0.0001) or not self:IsEqual(pos.y, prewPos.y, 0.0001) then local notchStart, notchEnd = self:GetTimingMarkPos(moho, prewPos, pos, nextPos, (self.timingMarksSize / 2) * self.width, false, frame) pathLayerMesh:AddLonePoint(notchStart, 0) pathLayerMesh:AppendPoint(notchEnd, 0) prewPos = pos end end pathLayerMesh:SelectNone() end local totalPointsWithtimingMark = pathLayerMesh:CountPoints() for i = totalPoints, totalPointsWithtimingMark - 1 do local point = pathLayerMesh:Point(i) point.fSelected = true end local shapeID = moho:CreateShape(false, false, 0) if shapeID >= 0 then local shape = pathLayerMesh:Shape(shapeID) local lineWidth = self.width / 2 lineWidth = LM.Clamp(lineWidth, 0.25, 256) shape.fMyStyle.fLineWidth = lineWidth / moho.document:Height() shape.fMyStyle.fLineCol:SetValue(0, color) shape.fMyStyle.fLineCaps = 0 shape:MakePlain() end pathLayerMesh:SelectNone() end end if self.centerLine then for p = 1, #pointList.point do local linePoint = pathLayerMesh:Point(pointList.point[p]) linePoint.fSelected = true pathLayerMesh:SelectConnected() local pathColor = pointList.color[p] local secondColor = LM.rgb_color:new_local() if (pathColor.r + pathColor.g + pathColor.b) < 382 then secondColor.r = 255 secondColor.g = 255 secondColor.b = 255 secondColor.a = 255 else secondColor.r = 0 secondColor.g = 0 secondColor.b = 0 secondColor.a = 255 end local v = LM.Vector2:new_local() local vc1 = LM.ColorVector:new_local() local vc2 = LM.ColorVector:new_local() vc1:Set(pathColor) vc2:Set(secondColor) vc1 = (vc1 + vc2) / 2 local newColor = vc1:AsColorStruct() local shapeID = moho:CreateShape(false, false, 0) if shapeID >= 0 then local shape = pathLayerMesh:Shape(shapeID) local lineWidth = (self.width / 2) / 2 lineWidth = LM.Clamp(lineWidth, 0.1, 256) shape.fMyStyle.fLineWidth = lineWidth / moho.document:Height() shape.fMyStyle.fLineCol:SetValue(0, newColor) shape.fMyStyle.fLineCaps = 0 shape:MakePlain() end pathLayerMesh:SelectNone() end end if wireframe then moho.view:SetQualityFlags(currentQualityFlags) end MOHO.MohoGlobals.MuteSoundtrack = isSoundtrackMuted moho.view:DrawMe() moho:SetSelLayer(curLayer) if curAction ~= '' then self:ReturnToAction(moho, curAction, curLayer) end moho:SetCurFrame(curFrame) pathLayer:UpdateCurFrame() curLayer:UpdateCurFrame() moho:UpdateUI() moho.view:DrawMe() end function MR_Path:DeletePathLayer(moho) self:CleanUpInvalidTargets(moho) local pathLayer, targetLayer = self:CheckExistPathLayer(moho) if pathLayer then moho.document:SetDirty() moho.document:PrepUndo() local curAction = moho.layer:CurrentAction() local curFrame = moho.frame local curLayer = moho.layer moho:DeleteLayer(pathLayer) self:ReturnToAction(moho, curAction, curLayer) if curFrame > 0 then moho:SetCurFrame(0) moho:SetCurFrame(curFrame) elseif curFrame == 0 then moho:SetCurFrame(1) moho:SetCurFrame(curFrame) end moho.layer:UpdateCurFrame() moho.view:DrawMe() moho:UpdateUI() moho:UpdateUI() moho.view:DrawMe() end end function MR_Path:ClearPath(moho) self:CleanUpInvalidTargets(moho) local pathLayer, targetLayer = self:CheckExistPathLayer(moho) if pathLayer then moho.document:SetDirty() moho.document:PrepUndo(pathLayer, true) pathLayerMesh = moho:LayerAsVector(pathLayer):Mesh() pathLayerMesh:Clear() pathLayer:UpdateCurFrame() moho:UpdateUI() moho.view:DrawMe() end end function MR_Path:CreateNewLayer(moho) local pathLayer = moho:CreateNewLayer(MOHO.LT_VECTOR, false) local scriptData = pathLayer:ScriptData() scriptData:Set("MR Path Layer", true) self.numVers = {} local vers = moho:AppVersion() for n in string.gmatch (vers, "%d+") do table.insert(self.numVers, tonumber(n)) end if self.numVers[1] == 13 and self.numVers[2] == 5 then if self.numVers[3] ~= nil then if self.numVers[3] >= 2 then pathLayer:SetIgnoredByLayerPicker(true) end end elseif self.numVers[1] == 13 and self.numVers[2] > 5 then pathLayer:SetIgnoredByLayerPicker(true) elseif self.numVers[1] > 13 then pathLayer:SetIgnoredByLayerPicker(true) end pathLayer:SetName('Path') pathLayer.fAlpha:SetValue(0, self.pathOpacity / 100) pathLayer:SetEditOnly(true) pathLayer:SetImmuneToCamera (true) return pathLayer end function MR_Path:GetLayerByUUID(moho, uuid) local count = 0 repeat local layer = moho.document:LayerByAbsoluteID(count) if layer then count = count + 1 if layer:UUID() == uuid then return layer end end until not layer return nil end function MR_Path:GetGlobalPos(moho, layer, pos) local globalPos = LM.Vector2:new_local() globalPos:Set(pos) local layerMatrix = LM.Matrix:new_local() layer:GetFullTransform(moho.frame, layerMatrix, moho.document) layerMatrix:Transform(globalPos) return globalPos end function MR_Path:CheckExistPathLayer(moho) local topLayer = moho.document:Layer(moho.document:CountLayers()-1) local pathLayer = nil local targetLayer = nil local scriptData = topLayer:ScriptData() if (scriptData:HasKey("MR Path Layer")) then pathLayer = moho:LayerAsVector(topLayer) elseif (scriptData:HasKey("MR Guides Layer")) then topLayer = moho.document:Layer(moho.document:CountLayers()-2) scriptData = topLayer:ScriptData() if (scriptData:HasKey("MR Path Layer")) then pathLayer = moho:LayerAsVector(topLayer) else targetLayer = topLayer end elseif (scriptData:HasKey("MR Overlay Layer")) then topLayer = moho.document:Layer(moho.document:CountLayers()-1) scriptData = topLayer:ScriptData() if (scriptData:HasKey("MR Path Layer")) then pathLayer = moho:LayerAsVector(topLayer) else targetLayer = topLayer end else targetLayer = topLayer end return pathLayer, targetLayer end function MR_Path:GetTimingMarkPos(moho, vec1, vec2, vec3, size, firstMarker, frame) local adaptedSize = size / 100 local v1 = vec1 local v2 = vec2 v2 = v2 - v1 local angle = math.atan2(v2.y, v2.x) local v2 = vec2 local v3 = vec3 v3 = v3 - v2 local angle2 = math.atan2(v3.y, v3.x) local p1 = vec1 local p2 = vec2 local p3 = vec3 local v1 = {x = p1.x - p2.x, y = p1.y - p2.y} local v2 = {x = p3.x - p2.x, y = p3.y - p2.y} local v1_length = math.sqrt(v1.x^2 + v1.y^2) local v2_length = math.sqrt(v2.x^2 + v2.y^2) local dot_product = v1.x * v2.x + v1.y * v2.y local cos_angle = dot_product / (v1_length * v2_length) cos_angle = math.max(-1, math.min(1, cos_angle)) local angle3 = math.acos(cos_angle) if not firstMarker then if angle3 == 0 or (self:IsEqual(vec2.x, vec3.x, 0.0001) and self:IsEqual(vec2.y, vec3.y, 0.0001)) then angle = angle + math.pi / 2 else local delta = angle2 - angle if delta > math.pi then delta = delta - 2 * math.pi elseif delta < -math.pi then delta = delta + 2 * math.pi elseif delta == 0 then delta = delta - 2 * math.pi end if delta > 0 then angle = angle - (angle3 / 2) elseif delta < 0 then angle = angle + (angle3 / 2) end end else angle = angle + math.pi / 2 end local vec3 = LM.Vector2:new_local() vec3.x = vec2.x + adaptedSize * math.cos(angle) vec3.y = vec2.y + adaptedSize * math.sin(angle) local vec4 = LM.Vector2:new_local() vec4.x = vec2.x + adaptedSize * math.cos(angle + math.pi) vec4.y = vec2.y + adaptedSize * math.sin(angle + math.pi) return vec3, vec4 end function MR_Path:IsEqual(a, b, epsilon) local absA = math.abs(a) local absB = math.abs(b) local diff = math.abs(a - b) if a == b then return true elseif diff < epsilon then return true else return false end end function MR_Path:ReturnToAction(moho, action, layer) if action ~= '' then local parentGroup = layer:Parent() local skelLayer = nil if layer:LayerType() == 4 and layer:HasAction(action) then skelLayer = layer elseif parentGroup ~= nil then local targetGroup = parentGroup repeat if targetGroup:LayerType() == 4 then -- LT_BONE if targetGroup:HasAction(action) then skelLayer = targetGroup end end targetGroup = targetGroup:Parent() until targetGroup == nil end moho:SetSelLayer(skelLayer) moho.document:SetCurrentDocAction(action) skelLayer:ActivateAction(action) if skelLayer ~= layer then moho:SetSelLayer(layer) layer:ActivateAction(action) end else moho:SetSelLayer(layer) end end -- ************************************************** -- Localization -- ************************************************** function MR_Path:Localize(text) local phrase = {} phrase['Description'] = 'Draw bone and point trajectories over all layers for spacing analysis.' phrase['UILabel'] = 'MR Path 1.1' phrase['Track bone tip'] = 'Track bone tip' phrase['Add to tracking'] = 'Add to tracking' phrase['Remove from tracking'] = 'Remove from tracking' phrase['Clear tracking list'] = 'Clear tracking list' phrase['Create path'] = 'Create path' phrase['Clear path'] = 'Clear path' phrase['Delete path layer'] = 'Delete path layer' phrase['Show hide'] = 'Show hide' phrase['Path Opacity:'] = 'Opacity:' phrase['Opacity for all Paths'] = 'Path layer opacity' phrase['Width:'] = 'Width:' phrase['Width for all Paths'] = 'Width for all Paths' phrase['Color'] = 'Color:' phrase['Draw center line'] = 'Draw center line' phrase['Draw timing marks'] = 'Draw tick marks' phrase['Size:'] = 'Size:' phrase['Timing marks size'] = 'Timing marks size' phrase['Path Color Tooltip'] = 'Path Color' phrase['Adjacent frames range'] = 'Adjacent frames range' phrase['Adjacent frames'] = 'Adjacent frames' phrase['Playback range'] = 'Playback range' phrase['Project range'] = 'Project range' phrase['From to range'] = 'From/To range' phrase['From'] = 'From' phrase['To'] = 'To' phrase['IntervalText'] = 'Interval:' phrase['Path Target Manager'] = 'Path Target Manager' phrase['Path target list is empty'] = 'Path target list is empty' phrase['Reset settings'] = 'Click to reset settings. Alt + Click to copy settings to other targets.' phrase['Settings changed'] = 'The settings of this target have been changed.' phrase['Settings not changed'] = 'The settings of this target have not been changed.' phrase['Randomize colors'] = 'Randomize colors' phrase['Targets limit exceeded alert 1'] = 'Targets limit exceeded.' phrase['Targets limit exceeded alert 2'] = 'You have added the maximum number of targets ('..self.maxTargets..'). Number of targets not added:' return phrase[text] end
MR Path
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Jun 25 2023, 07:52
Last modified: Sep 12 2023, 11:51
Draw bone and point trajectories over all layers for spacing analysis.
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: 554

MR Path
Listed
Author: eugenebabich
View Script
Script type: Tool
Uploaded: Jun 25 2023, 07:52
Last modified: Sep 12 2023, 11:51
Draw bone and point trajectories over all layers for spacing analysis.
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: 554