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

ScriptName = "MR_Guides"

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

MR_Guides = {}

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

function MR_Guides:Version()
	return '1.3'
end

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

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

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

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

function MR_Guides:IsRelevant(moho)
	return true
end

function MR_Guides:IsEnabled(moho)
	return true
end

function MR_Guides:SupportsGPUMode(moho)
	return false
end
-- **************************************************
-- Recurring Values
-- **************************************************

MR_Guides.createVGuidBtn = true
MR_Guides.createHGuidBtn = false
MR_Guides.moveGuideBtn = false
MR_Guides.deleteGuideBtn = false
MR_Guides.opacity = 100
MR_Guides.width = 1
MR_Guides.cursorVec = LM.Vector2:new_local()
MR_Guides.lastCursorVec = LM.Vector2:new_local()
MR_Guides.druggingMouse = false
MR_Guides.offset = LM.Vector2:new_local()
MR_Guides.guideColorR = 84
MR_Guides.guideColorG = 212
MR_Guides.guideColorB = 196
MR_Guides.guideColorA = 255
MR_Guides.guideStartPoint = nil
MR_Guides.guideEndPoint = nil
MR_Guides.direction = true
MR_Guides.selectionMode = false
MR_Guides.selRect = LM.Rect:new_local()

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

function MR_Guides:LoadPrefs(prefs)
    self.createVGuidBtn = prefs:GetBool("MR_Guides.createVGuidBtn", true)
    self.createHGuidBtn = prefs:GetBool("MR_Guides.createHGuidBtn", false)
    self.moveGuideBtn = prefs:GetBool("MR_Guides.moveGuideBtn", false)
    self.deleteGuideBtn = prefs:GetBool("MR_Guides.deleteGuideBtn", false)
    self.opacity = prefs:GetInt("MR_Guides.opacity", 100)
    self.width = prefs:GetFloat("MR_Guides.width", 1)
	self.guideColorR = prefs:GetInt("MR_Guides.guideColorR", 84)
	self.guideColorG = prefs:GetInt("MR_Guides.guideColorG", 212)
	self.guideColorB = prefs:GetInt("MR_Guides.guideColorB", 196)
	self.guideColorB = prefs:GetInt("MR_Guides.guideColorA", 255)
end

function MR_Guides:SavePrefs(prefs)
    prefs:SetBool("MR_Guides.createVGuidBtn", self.createVGuidBtn)
    prefs:SetBool("MR_Guides.createHGuidBtn", self.createHGuidBtn)
    prefs:SetBool("MR_Guides.moveGuideBtn", self.moveGuideBtn)
    prefs:SetBool("MR_Guides.deleteGuideBtn", self.deleteGuideBtn)
    prefs:SetInt("MR_Guides.opacity", self.opacity)
    prefs:SetFloat("MR_Guides.width", self.width)
	prefs:SetInt("MR_Guides.strokeColorR", self.guideColorR)
	prefs:SetInt("MR_Guides.strokeColorG", self.guideColorG)
	prefs:SetInt("MR_Guides.strokeColorB", self.guideColorB)
	prefs:SetInt("MR_Guides.strokeColorA", self.guideColorA)
end

function MR_Guides:ResetPrefs()
    self.createVGuidBtn = true
    self.createHGuidBtn = false
    self.moveGuideBtn = false
    self.deleteGuideBtn = false
    self.opacity = 100
    self.width = 1
	self.guideColorR = 84
	self.guideColorG = 212
	self.guideColorB = 196
	self.guideColorA = 255
end

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

function MR_Guides:OnMouseDown(moho, mouseEvent)
	local mesh = moho:Mesh()
	self.selectionMode = false
	if mouseEvent.ctrlKey then
		self.selectionMode = true
		if mesh then
			if not mouseEvent.shiftKey and not mouseEvent.altKey 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()
		return
	end
	
	self.curLayer = moho.layer
	self.curAction = moho.layer:CurrentAction()
	self.curFrame = moho.frame
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		moho.document:PrepUndo(topLayer)
	else
		moho.document:PrepUndo(nil)
	end
	moho.document:SetDirty()
	
	local curveID = -1
	local segID = -1
	
	if self.moveGuideBtn then
		if (scriptData:HasKey("MR Guides Layer")) then
			self.guidesLayer = topLayer
			moho:SetSelLayer(self.guidesLayer)
			local mesh = moho:LayerAsVector(self.guidesLayer):Mesh()
			local shapesWidth = {}
			for i = 0, mesh:CountShapes() -1 do
				local shape = mesh:Shape(i)
				table.insert(shapesWidth, shape.fMyStyle.fLineWidth)
				shape.fMyStyle.fLineWidth = (10 / moho.document:Height())/moho.view:Graphics():CurrentScale(false)
			end
			local shapeID = mouseEvent.view:PickShape(mouseEvent.pt, -1, 5)
			for i = 0, mesh:CountShapes() -1 do
				local shape = mesh:Shape(i)
				shape.fMyStyle.fLineWidth = shapesWidth[i + 1]
			end	
			mesh:SelectNone()
			if shapeID >= 0 then
				local shape = mesh:Shape(shapeID)
				for i = 0, shape:CountEdges() - 1 do
					local curveID, segID = shape:GetEdge(i, curveID, segID)
					curve = mesh:Curve(curveID)
					curve:Point(0).fSelected = true
					curve:Point(1).fSelected = true
					self.guideStartPoint = curve:Point(0)
					self.guideEndPoint = curve:Point(1)
					self.guideStartPoint.fTempPos = self.guideStartPoint.fAnimPos:GetValue(0)
					self.guideEndPoint.fTempPos = self.guideEndPoint.fAnimPos:GetValue(0)
					if self.guideStartPoint.fAnimPos:GetValue(0).x ~= self.guideEndPoint.fAnimPos:GetValue(0).x then
						self.direction = false
					elseif self.guideStartPoint.fAnimPos:GetValue(0).y ~= self.guideEndPoint.fAnimPos:GetValue(0).y then
						self.direction = true
					end
					self:ReturnToAction(moho)
				end
			else
				self:ReturnToAction(moho)
				self.guideStartPoint = nil
				self.guideEndPoint = nil
				mesh:SelectNone()
				return
			end
		else
			return
		end
	end
	self.cursorVec:Set(self:GetGlobalPointPos(moho, mouseEvent.drawingVec, moho.layer))
end

function MR_Guides:OnMouseMoved(moho, mouseEvent)
	if self.selectionMode then
		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()
		return
	end

	if self.createHGuidBtn or self.createVGuidBtn then
		self.druggingMouse = true
	end
	self.cursorVec:Set(self:GetGlobalPointPos(moho, mouseEvent.drawingVec, moho.layer))
	if self.moveGuideBtn then
		if self.guideStartPoint == nil or self.guideEndPoint == nil then
			return
		end
		self.offset:Set(self:GetGlobalPointPos(moho, mouseEvent.drawingVec, self.curLayer) - self:GetGlobalPointPos(moho, mouseEvent.drawingStartVec, self.curLayer))
		if self.direction then
			self.offset:Set(self.offset.x, 0)
		else
			self.offset:Set(0, self.offset.y)
		end	
		self.guideStartPoint.fPos = self.guideStartPoint.fTempPos + self.offset
		self.guideEndPoint.fPos = self.guideEndPoint.fTempPos + self.offset
		self.guideStartPoint.fAnimPos:SetValue(0, self.guideStartPoint.fTempPos + self.offset)
		self.guideEndPoint.fAnimPos:SetValue(0, self.guideEndPoint.fTempPos + self.offset)
	end
	mouseEvent.view:DrawMe()
end

function MR_Guides:OnMouseUp(moho, mouseEvent)
	if self.selectionMode then
		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 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.selectionMode = 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_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.selectionMode = false
				end
			end	
		end
		moho:UpdateSelectedChannels()
		return
	end

	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	moho:SetSelLayer(topLayer)
	if self.createHGuidBtn then
		self:CreateUpdateGuidsLayer(moho, true)
	elseif	self.createVGuidBtn then
		self:CreateUpdateGuidsLayer(moho, false)
	elseif self.deleteGuideBtn then
		self:DeleteGuide(moho, mouseEvent)
	end
	self.druggingMouse = false
	if self.moveGuideBtn then
		self:ReturnToAction(moho)
	end
	self.lastCursorVec:Set(self.cursorVec)
	self.guidesLayer:UpdateCurFrame()
	self.curLayer:UpdateCurFrame()
	moho:UpdateUI()
	moho:UpdateSelectedChannels()
end

function MR_Guides:DrawMe(moho, view)
	if self.selectionMode then
		local g = view:Graphics()
		g:SelectionRect(self.selRect)
		return
	end

	local g = view:Graphics()
	local min = LM.Vector2:new_local()
	local max = LM.Vector2:new_local()
	local centerVec = LM.Vector2:new_local()
	local v = LM.Vector2:new_local()
	local color = LM.rgb_color:new_local()
	color.r = self.guideColorR
	color.g = self.guideColorG
	color.b = self.guideColorB
	color.a = self.guideColorA
	
	if self.druggingMouse then
		g:Push()
		local width = (g:Width() / moho.document:Width())
		local lineWidth = self.width
		lineWidth = LM.Clamp(lineWidth, 0.25, 256)
		g:SetPenWidth(((lineWidth / 1.25) * g:CurrentScale(false)) * width)
		g:SetColor(color)
		if self.createVGuidBtn then
			g:DrawLine(-10/g:CurrentScale(false), self.cursorVec.y, 10/g:CurrentScale(false), self.cursorVec.y)
		elseif self.createHGuidBtn then
			g:DrawLine(self.cursorVec.x, -10/g:CurrentScale(false), self.cursorVec.x, 10/g:CurrentScale(false))
		end	
		g:Pop()
	end
end

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

MR_Guides.CREATE_GUIDE_H = MOHO.MSG_BASE
MR_Guides.CREATE_GUIDE_V = MOHO.MSG_BASE + 1
MR_Guides.MOVE_GUIDE = MOHO.MSG_BASE + 2
MR_Guides.DELETE_GUIDE = MOHO.MSG_BASE + 3
MR_Guides.DELETE_LAST_GUIDE = MOHO.MSG_BASE + 4
MR_Guides.DELETE_GUIDES_LAYER = MOHO.MSG_BASE + 5
MR_Guides.OPACITY = MOHO.MSG_BASE + 6
MR_Guides.WIDTH = MOHO.MSG_BASE + 7
MR_Guides.COLOR = MOHO.MSG_BASE + 8
MR_Guides.VISIBILITY = MOHO.MSG_BASE + 9
MR_Guides.CREATE_CENTER_SELECTED_GUIDE_H = MOHO.MSG_BASE + 10
MR_Guides.CREATE_CENTER_SELECTED_GUIDE_V = MOHO.MSG_BASE + 11

function MR_Guides:DoLayout(moho, layout)
	layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)

	self.createGuideHButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_create_guide_h', self:Localize('Create Horizontal Guide'), true, self.CREATE_GUIDE_H, false)
	self.createGuideHButton:SetAlternateMessage(self.CREATE_CENTER_SELECTED_GUIDE_H)
    layout:AddChild(self.createGuideHButton, LM.GUI.ALIGN_LEFT, 0)

    self.createGuideVButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_create_guide_v', self:Localize('Create Vertical Guide'), true, self.CREATE_GUIDE_V, false)
	self.createGuideVButton:SetAlternateMessage(self.CREATE_CENTER_SELECTED_GUIDE_V)
    layout:AddChild(self.createGuideVButton, LM.GUI.ALIGN_LEFT, 0)

    self.moveGuideButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_move_guide', self:Localize('Move Existing Guide'), true, self.MOVE_GUIDE, false)
    layout:AddChild(self.moveGuideButton, LM.GUI.ALIGN_LEFT, 0)

    self.deleteGuideButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_delete_guide', self:Localize('Delete Guide'), true, self.DELETE_GUIDE, false)
    layout:AddChild(self.deleteGuideButton, LM.GUI.ALIGN_LEFT, 0)

    self.deleteLastGuideButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_delete_last_guide', self:Localize('Delete Last Guide'), false, self.DELETE_LAST_GUIDE, false)
    layout:AddChild(self.deleteLastGuideButton, LM.GUI.ALIGN_LEFT, 0)
	
	self.deleteGuidesLayerButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_delete_guides_layer', self:Localize('Delete Guides Layer'), false, self.DELETE_GUIDES_LAYER, false)
    layout:AddChild(self.deleteGuidesLayerButton, LM.GUI.ALIGN_LEFT, 0)
	
    layout:AddChild(LM.GUI.Divider(true), LM.GUI.ALIGN_FILL)
	
	self.guidesVisibilityButton = LM.GUI.ImageButton('ScriptResources/mr_guides/mr_visibility', self:Localize('Guides Visibility'), true, self.VISIBILITY, false)
    layout:AddChild(self.guidesVisibilityButton, LM.GUI.ALIGN_LEFT, 0)

    self.opacityInput = LM.GUI.TextControl(30, '100', self.OPACITY, LM.GUI.FIELD_INT, self:Localize('Opacity:'))
    layout:AddChild(self.opacityInput, LM.GUI.ALIGN_LEFT, 0)
    self.opacityInput:SetToolTip(self:Localize('Opacity of new guides'))

    self.widthInput = LM.GUI.TextControl(50, '1', self.WIDTH, LM.GUI.FIELD_UFLOAT, self:Localize('Width:'))
	self.widthInput:SetWheelInc(1.0)
	self.widthInput:SetWheelInteger(true)
    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.guideColorSwatch = LM.GUI.ShortColorSwatch(true, self.COLOR)
	layout:AddChild(self.guideColorSwatch, LM.GUI.ALIGN_LEFT)
	self.guideColorSwatch:SetToolTip(MR_Guides:Localize('Guide Color Tooltip'))
end

function MR_Guides:UpdateWidgets(moho)
    self.createGuideHButton:SetValue(self.createVGuidBtn)
    self.createGuideVButton:SetValue(self.createHGuidBtn)
    self.moveGuideButton:SetValue(self.moveGuideBtn)
    self.deleteGuideButton:SetValue(self.deleteGuideBtn)
    self.opacityInput:SetValue(self.opacity)
    self.widthInput:SetValue(self.width)
	local guideColor = LM.rgb_color:new_local()
	guideColor.r = self.guideColorR
	guideColor.g = self.guideColorG
	guideColor.b = self.guideColorB
	guideColor.a = self.guideColorA
	self.guideColorSwatch:SetValue(guideColor)
	self.guidesLayer = self:CheckExistGuidesLayer(moho)
	if self.guidesLayer == nil then
		self.moveGuideButton:Enable(false)
		self.deleteGuideButton:Enable(false)
		self.opacityInput:Enable(false)
		self.deleteLastGuideButton:Enable(false)
		self.deleteGuidesLayerButton:Enable(false)
		self.createVGuidBtn = true
		self.createHGuidBtn = false
		self.moveGuideBtn = false
		self.deleteGuideBtn = false
		self.createGuideHButton:SetValue(self.createVGuidBtn)
		self.createGuideVButton:SetValue(self.createHGuidBtn)
		self.moveGuideButton:SetValue(self.moveGuideBtn)
		self.deleteGuideButton:SetValue(self.deleteGuideBtn)
		self.guidesVisibilityButton:SetValue(false)
		self.guidesVisibilityButton:Enable(false)
	else
		local mesh = self.guidesLayer:Mesh()
		self.guidesVisibilityButton:SetValue(self.guidesLayer:IsVisible())
		if mesh:CountPoints() < 2 then
			self.createVGuidBtn = true
			self.createHGuidBtn = false
			self.moveGuideBtn = false
			self.deleteGuideBtn = false
			self.createGuideHButton:SetValue(self.createVGuidBtn)
			self.createGuideVButton:SetValue(self.createHGuidBtn)
			self.moveGuideButton:SetValue(self.moveGuideBtn)
			self.deleteGuideButton:SetValue(self.deleteGuideBtn)
			self.moveGuideButton:Enable(false)
			self.deleteGuideButton:Enable(false)
			self.deleteLastGuideButton:Enable(false)
			self.guidesVisibilityButton:Enable(false)
			self.guidesVisibilityButton:SetValue(false)
		else
			self.moveGuideButton:Enable(true)
			self.deleteGuideButton:Enable(true)
			self.deleteLastGuideButton:Enable(true)
			self.guidesVisibilityButton:Enable(true)
		end
		self.opacityInput:Enable(true)
		self.deleteGuidesLayerButton:Enable(true)
		self.opacityInput:SetValue(self.guidesLayer.fAlpha:GetValue(0) * 100)
	end
end

function MR_Guides:HandleMessage(moho, view, msg)
    if msg == self.CREATE_GUIDE_H then
        self.createVGuidBtn = true
		self.createHGuidBtn = false
		self.moveGuideBtn = false
		self.deleteGuideBtn = false
		self.createGuideHButton:SetValue(true)
		self.createGuideVButton:SetValue(false)
		self.moveGuideButton:SetValue(false)
		self.deleteGuideButton:SetValue(false)
	elseif msg == self.CREATE_CENTER_SELECTED_GUIDE_H then
		self:CreateCenterSelectedGuide(moho, false)
    elseif msg == self.CREATE_GUIDE_V then
        self.createVGuidBtn = false
		self.createHGuidBtn = true
		self.moveGuideBtn = false
		self.deleteGuideBtn = false
		self.createGuideHButton:SetValue(false)
		self.createGuideVButton:SetValue(true)
		self.moveGuideButton:SetValue(false)
		self.deleteGuideButton:SetValue(false)
	elseif msg == self.CREATE_CENTER_SELECTED_GUIDE_V then
		self:CreateCenterSelectedGuide(moho, true)
    elseif msg == self.MOVE_GUIDE then
        self.createVGuidBtn = false
		self.createHGuidBtn = false
		self.moveGuideBtn = true
		self.deleteGuideBtn = false
		self.createGuideHButton:SetValue(false)
		self.createGuideVButton:SetValue(false)
		self.moveGuideButton:SetValue(true)
		self.deleteGuideButton:SetValue(false)
    elseif msg == self.DELETE_GUIDE then
        self.createVGuidBtn = false
		self.createHGuidBtn = false
		self.moveGuideBtn = false
		self.deleteGuideBtn = true
		self.createGuideHButton:SetValue(false)
		self.createGuideVButton:SetValue(false)
		self.moveGuideButton:SetValue(false)
		self.deleteGuideButton:SetValue(true)
		self:UpdateWidgets(moho)
    elseif msg == self.DELETE_LAST_GUIDE then
        self:DeleteLastGuide(moho)
		self:UpdateWidgets(moho)
	elseif msg == self.DELETE_GUIDES_LAYER then
        self:DeleteGuideLayer(moho)	
	elseif msg == self.VISIBILITY then
        self.guidesLayer = self:CheckExistGuidesLayer(moho)
		self.guidesLayer:SetVisible(self.guidesVisibilityButton:Value())
		moho:UpdateUI()
    elseif msg == self.OPACITY then
        self.opacity = LM.Clamp(self.opacityInput:Value(), 0, 100)
		self.opacityInput:SetValue(LM.Clamp(self.opacity, 0, 100))
		self:SetGuidesLayerOpacity(moho)
		moho:UpdateUI()
    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.guideColorSwatch:Value()
		self.guideColorR = colorSwatchValue.r
		self.guideColorG = colorSwatchValue.g
		self.guideColorB = colorSwatchValue.b
		self.guideColorA = colorSwatchValue.a
    end
end

function MR_Guides:CheckExistGuidesLayer(moho)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local guidesLayer = nil
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		guidesLayer = moho:LayerAsVector(topLayer)
	end	
	return guidesLayer
end

function MR_Guides:CreateGuide(moho, direction)
	local layer = moho:LayerAsVector(self.guidesLayer)
	local mesh = layer:Mesh()
	local n = mesh:CountPoints()
	local startPointPos = LM.Vector2:new_local()
	local endPointPos = LM.Vector2:new_local()
	if direction then
		startPointPos:Set(- 15, self.cursorVec.y)
		endPointPos:Set(15, self.cursorVec.y)
	else
		startPointPos:Set(self.cursorVec.x, - 5)
		endPointPos:Set(self.cursorVec.x, 5)
	end
	
	local isGuideNew = true
	
	if direction then
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if curve:Point(0) and curve:Point(1) then
				local curveStartPointPos = LM.Vector2:new_local()
				local curveEndPointPos = LM.Vector2:new_local()
				curveStartPointPos:Set(self:GetGlobalPointPos(moho, curve:Point(0).fPos, self.guidesLayer))
				curveEndPointPos:Set(self:GetGlobalPointPos(moho, curve:Point(1).fPos, self.guidesLayer))
				if curveStartPointPos.y == startPointPos.y and curveEndPointPos.y == endPointPos.y then
					isGuideNew = false
					break
				end
			end
		end
	else
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if curve:Point(0) and curve:Point(1) then
				local curveStartPointPos = LM.Vector2:new_local()
				local curveEndPointPos = LM.Vector2:new_local()
				curveStartPointPos:Set(self:GetGlobalPointPos(moho, curve:Point(0).fPos, self.guidesLayer))
				curveEndPointPos:Set(self:GetGlobalPointPos(moho, curve:Point(1).fPos, self.guidesLayer))
				if curveStartPointPos.x == startPointPos.x and curveEndPointPos.x == endPointPos.x then
					isGuideNew = false
					break
				end
			end
		end
	end	
	if isGuideNew then
		mesh:SelectNone()
		
		local color = LM.rgb_color:new_local()
		color.r = self.guideColorR
		color.g = self.guideColorG
		color.b = self.guideColorB
		color.a = self.guideColorA
		
		if mesh:CountPoints() < 2 then
			mesh:AddLonePoint(startPointPos, 0)
			mesh:AppendPoint(endPointPos, 0)
			mesh:Point(n):SetCurvature(MOHO.PEAKED, 0)
			mesh:Point(n + 1):SetCurvature(MOHO.PEAKED, 0)
			mesh:Point(n + 1).fSelected = true
			mesh:SelectConnected()
			local shapeID = moho:CreateShape(false, false, 0)
			if shapeID >= 0 then
				local shape = mesh:Shape(shapeID)
				if mesh:CountShapes() > 0 then
					local style = moho:CurrentEditStyle()
					if (style ~= nil) then
						local lineWidth = self.width
						lineWidth = LM.Clamp(lineWidth, 0.25, 256)
						style.fLineWidth = lineWidth / moho.document:Height()
						style.fLineCol:SetValue(0, color)
					end
				end		
				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:MakePlain()
			end
		else
			mesh:Point(n - 1).fSelected = true
			mesh:SelectConnected()
			moho:Copy(mesh)
			moho:Paste()
			mesh:SelectNone()
			local startPoint = mesh:Point(mesh:CountPoints() - 2)
			local endPoint = mesh:Point(mesh:CountPoints() - 1)
			startPoint.fSelected = true
			endPoint.fSelected = true
			startPoint.fAnimPos:SetValue(0, startPointPos)
			endPoint.fAnimPos:SetValue(0, endPointPos)
			startPoint:SetCurvature(MOHO.PEAKED, 0)
			endPoint:SetCurvature(MOHO.PEAKED, 0)
			for i = 0, mesh:CountShapes() - 1 do
				local shape = mesh:Shape(i)
				local pointId = mesh:PointID(endPoint)
				if (shape.fHasOutline and shape:ContainsPoint(pointId)) then
					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:MakePlain()
				end
			end
		end	
		mesh:SelectNone()
	end	
end

function MR_Guides:CreateUpdateGuidsLayer(moho, direction)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		local topLayerMesh = moho:LayerAsVector(topLayer):Mesh()
		if topLayerMesh ~= nil then
			self.guidesLayer = moho:LayerAsVector(topLayer)
			self.guidesLayer:SetVisible(true)
		end
	else
		self.guidesLayer = moho:CreateNewLayer(MOHO.LT_VECTOR)
		local scriptData = self.guidesLayer:ScriptData()
		scriptData:Set("MR Guides Layer", true)
		local numVers = {}
		local vers = moho:AppVersion()
		for n in string.gmatch (vers, "%d+") do
			table.insert(numVers, tonumber(n))
		end
		if numVers[1] == 13 and numVers[2] == 5 then
			if numVers[3] ~= nil then
				if numVers[3] >= 2 then
					self.guidesLayer:SetIgnoredByLayerPicker(true)
				end
			end
		elseif numVers[1] == 13 and numVers[2] > 5 then
			self.guidesLayer:SetIgnoredByLayerPicker(true)
		elseif numVers[1] > 13 then
			self.guidesLayer:SetIgnoredByLayerPicker(true)	
		end
	end	

	self.guidesLayer:SetName('Guides')
	self.guidesLayer.fAlpha:SetValue(0, self.opacity / 100)
	self.guidesLayer:SetEditOnly(true)
	self.guidesLayer:SetImmuneToCamera(true)
	local newMesh = moho:LayerAsVector(self.guidesLayer):Mesh()
	local newMesh = moho:LayerAsVector(self.guidesLayer):Mesh()
	
	if not direction then
		self:CreateGuide(moho, true)
	elseif direction then
		self:CreateGuide(moho, false)
	end

	self:ReturnToAction(moho)
	
	moho:SetCurFrame(self.curFrame)
end

function MR_Guides:GetGlobalPointPos(moho, pointPosition, layer)
	local pointPos = LM.Vector2:new_local()
	pointPos:Set(pointPosition)
	local layerMatrix = LM.Matrix:new_local()
	layer:GetFullTransform(moho.frame, layerMatrix, moho.document)
	layerMatrix:Transform(pointPos)
	return pointPos
end

function MR_Guides:ReturnToAction(moho)
	if self.curAction ~= '' then
		local parentGroup = self.curLayer:Parent()
		local skelLayer = nil
		
		if self.curLayer:LayerType() == 4 and self.curLayer:HasAction(self.curAction) then
			skelLayer = self.curLayer
		elseif parentGroup ~= nil then
			local targetGroup = parentGroup
			repeat
				if targetGroup:LayerType() == 4 then -- LT_BONE
					if targetGroup:HasAction(self.curAction) then
						skelLayer = targetGroup
					end	
				end
				targetGroup = targetGroup:Parent()
			until targetGroup == nil
		end
		if skelLayer then
			moho:SetSelLayer(skelLayer)
			moho.document:SetCurrentDocAction(self.curAction)
			skelLayer:ActivateAction(self.curAction)
			if skelLayer ~= self.curLayer then
				moho:SetSelLayer(self.curLayer)
				self.curLayer:ActivateAction(self.curAction)
			end
		end	
		moho:SetCurFrame(self.curFrame)
	else	
		moho:SetSelLayer(self.curLayer)
	end
end

function MR_Guides:DeleteGuide(moho, mouseEvent)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	self.guidesLayer = nil
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		self.guidesLayer = topLayer
	else
		return
	end
	
	local layer = moho:LayerAsVector(self.guidesLayer)
	local mesh = layer:Mesh()
	
	if mesh:CountPoints() < 1 then
		return
	end
	
	moho:SetSelLayer(self.guidesLayer)
	mesh:SelectNone()
	
	local curveID = -1
	local segID = -1
	
	local shapesWidth = {}
	for i = 0, mesh:CountShapes() -1 do
		local shape = mesh:Shape(i)
		table.insert(shapesWidth, shape.fMyStyle.fLineWidth)
		shape.fMyStyle.fLineWidth = (10 / moho.document:Height())/moho.view:Graphics():CurrentScale(false)
		shape:MakePlain()
	end
	local shapeID = mouseEvent.view:PickShape(mouseEvent.pt, -1, 5)
	for i = 0, mesh:CountShapes() -1 do
		local shape = mesh:Shape(i)
		shape.fMyStyle.fLineWidth = shapesWidth[i + 1]
		shape:MakePlain()
	end	
	if (shapeID >= 0) then
		local shape = mesh:Shape(shapeID)
		for i = 0, shape:CountEdges() - 1 do
			curveID, segID = shape:GetEdge(i, curveID, segID)
			local curve = mesh:Curve(curveID)
			curve:Point(segID).fSelected = true
			mesh:SelectConnected()
			mesh:DeleteSelectedPoints()
		end
	end
	self:ReturnToAction(moho)
end

function MR_Guides:DeleteLastGuide(moho)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local guidesLayer = nil
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		guidesLayer = topLayer
		moho.document:PrepUndo(topLayer)
		moho.document:SetDirty()
	else
		return
	end
	
	local layer = moho:LayerAsVector(guidesLayer)
	local mesh = layer:Mesh()
	
	if mesh:CountPoints() < 1 then
		return
	end
	
	local point = mesh:Point(mesh:CountPoints()-1)
	mesh:SelectNone()
	point.fSelected = true
	mesh:SelectConnected()
	mesh:DeleteSelectedPoints()
end

function MR_Guides:SetGuidesLayerOpacity(moho)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local guidesLayer = nil
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		guidesLayer = topLayer
	else
		return
	end
	guidesLayer.fAlpha:SetValue(0, self.opacity / 100)
	guidesLayer:UpdateCurFrame()
	moho.view:DrawMe()
	moho:UpdateUI()
	moho:UpdateSelectedChannels()
end

function MR_Guides:DeleteGuideLayer(moho)
	local curFrame = moho.frame
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local guidesLayer = nil
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		guidesLayer = topLayer
	else
		return
	end
	
	self.curLayer = moho.layer
	if self.curLayer == guidesLayer then
		self.curLayer = moho.document:Layer(moho.document:CountLayers()-2)
	end	
	local curAction = moho.layer:CurrentAction()
	local curFrame = moho.frame
	moho.document:PrepUndo(nil)
	moho.document:SetDirty()
	moho:DeleteLayer(guidesLayer)
	self:ReturnToAction(moho)
	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:UpdateSelectedChannels()
end

function MR_Guides:CreateCenterSelectedGuide(moho, direction)
	local layer = moho:LayerAsVector(moho.layer)
	local topLayer = moho.document:Layer(moho.document:CountLayers()-1)
	local scriptData = topLayer:ScriptData()
	if (scriptData:HasKey("MR Guides Layer")) then
		moho.document:PrepUndo(topLayer)
	else
		moho.document:PrepUndo(nil)
	end
	self.curLayer = moho.layer
	self.curAction = moho.layer:CurrentAction()
	self.curFrame = moho.frame
	if layer then
		moho.document:SetDirty()
		local mesh = layer:Mesh()
		self.cursorVec:Set(self:GetGlobalPointPos(moho, mesh:SelectedCenter(), layer))
		moho:SetSelLayer(topLayer)
		self:CreateUpdateGuidsLayer(moho, direction)
		self.lastCursorVec:Set(self.cursorVec)
		self.guidesLayer:UpdateCurFrame()
		self.curLayer:UpdateCurFrame()
	else
		self.cursorVec:Set(self:GetGlobalPointPos(moho, self.curLayer:Origin(), self.curLayer))
		moho:SetSelLayer(topLayer)
		self:CreateUpdateGuidsLayer(moho, direction)
		self.lastCursorVec:Set(self.cursorVec)
		self.guidesLayer:UpdateCurFrame()
		self.curLayer:UpdateCurFrame()
	end
	moho:UpdateUI()
	moho:UpdateSelectedChannels()
end

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

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

	phrase['Description'] = 'Create and edit guides without leaving the current layer and action. Hold Ctrl to select points, Ctrl + Shift to add to selection and Ctrl + Alt to remove from selection.'
	phrase['UILabel'] = 'MR Guides 1.3'

	phrase['Create Horizontal Guide'] = 'Create horizontal guide (Alt + Click to create horizontal guide from selection center)'
	phrase['Create Vertical Guide'] = 'Create vertical guide (Alt + Click to create vertical guide from selection center)'
	phrase['Move Existing Guide'] = 'Move existing guide'
	phrase['Delete Guide'] = 'Delete guide'
	phrase['Delete Last Guide'] = 'Delete last guide'
	phrase['Opacity:'] = 'Guides opacity:'
	phrase['Opacity of new guides'] = 'Opacity of all guides.'
	phrase['Width:'] = 'Width:'
	phrase['Color'] = 'Color'
	phrase['Guide Color Tooltip'] = 'Guide color'
	phrase['Delete Guides Layer'] = 'Delete guides layer'
	phrase['Guides Visibility'] = 'Show/Hide guides'

	return phrase[text]
end

Icon
MR Guides
Listed

Script type: Tool

Uploaded: Oct 17 2021, 15:05

Last modified: Sep 12 2023, 11:58

Create and edit guides without leaving the current layer and action.
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: 2094