Scale artwork while retaining line width?

Wondering how to accomplish a certain animation task? Ask here.

Moderators: Víctor Paredes, Belgarath, slowtiger

Post Reply
User avatar
Manu
Posts: 327
Joined: Tue Aug 03, 2004 10:11 pm
Contact:

Scale artwork while retaining line width?

Post by Manu »

Is it possible to scale some artwork with the scale points tool and scale the line widths up at the same time?

Actually, I have already tried, so I know it's not possible. But is there a script that does this?
User avatar
PARKER
Posts: 1020
Joined: Sat Oct 25, 2008 4:26 am
Location: Animation World

Post by PARKER »

To increase the width of the line along with the size of your shape you should scale the layer not the shape, thats the only solution i can think about now.
User avatar
Manu
Posts: 327
Joined: Tue Aug 03, 2004 10:11 pm
Contact:

Post by Manu »

Right, better roll up my sleeves and learn a bit of Lua. That's too big a gap in the feature set I'd say.
User avatar
Manu
Posts: 327
Joined: Tue Aug 03, 2004 10:11 pm
Contact:

Post by Manu »

Well, I've got it working. Thanks to the wonders of copy and paste, I was able to make the first version in less than half an hour :D

All I did was make a replacement version of the lm_scale_points.lua script that scales the line together with the points. Here's the script for those of you who want to try it. All you have to do is replace the contents of the lm_scale_points.lua script with this. Made for 5.6, so no idea how it works for higher versions.

Note that I didn't add a button to switch the line scaling on and off. Also, I haven't commented the code in any way, which I know is big no-no, so only use this if you're curious and remember to back up the original lm_scale_points.lua first.

Code: Select all

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

ScriptName = "LM_ScalePoints"

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

LM_ScalePoints = {}

LM_ScalePoints.BASE_STR = 2280

function LM_ScalePoints:Name()
	return "Scale Points"
end

function LM_ScalePoints:Version()
	return "5.0"
end

function LM_ScalePoints:Description()
	return MOHO.Localize(self.BASE_STR, "Scale selected points (hold <alt> to preserve volume)")
end

function LM_ScalePoints:Creator()
	return "Lost Marble"
end

function LM_ScalePoints:UILabel()
	return(MOHO.Localize(self.BASE_STR + 1, "Scale Points"))
end

-- **************************************************
-- Recurring values
-- **************************************************

LM_ScalePoints.scalingDir = -1
LM_ScalePoints.minVec = LM.Vector2:new_local()
LM_ScalePoints.maxVec = LM.Vector2:new_local()

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

function LM_ScalePoints:IsEnabled(moho)
	if (moho:CountSelectedPoints() > 1) then
		return true
	end
	return false
end

function LM_ScalePoints:OnMouseDown(moho, mouseEvent)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	mesh:SelectedBounds(self.minVec, self.maxVec)
	self.centerVec = (self.minVec + self.maxVec) / 2

	-- First step - figure out which scaling handle is being dragged
	self.scalingDir = -1
	local boxW = (self.maxVec.x - self.minVec.x) / 2
	local boxH = (self.maxVec.y - self.minVec.y) / 2
	dx = mouseEvent.vec.x - self.centerVec.x
	dx = math.abs(dx)
	dy = mouseEvent.vec.y - self.centerVec.y
	dy = math.abs(dy)
	if ((dx > 0.8 * boxW) and (dx < 1.2 * boxW)) then
		if ((dy > 0.8 * boxH) and (dy < 1.2 * boxH)) then
			self.scalingDir = 2
		elseif (dy < 0.2 * boxH) then
			self.scalingDir = 0
		end
	elseif (dx < 0.2 * boxW) then
		if ((dy > 0.8 * boxH) and (dy < 1.2 * boxH)) then
			self.scalingDir = 1
		end
	end

	if ((self.scalingDir < 0) and (dx > 0.8 * boxW) and (dy > 0.8 * boxH)) then
		self.scalingDir = 2
	end

	if (self.scalingDir < 0) then
		return
	end

	moho.document:PrepUndo(moho.layer)
	moho.document:SetDirty()

	mesh:PrepMovePoints()
	
	self.selID = -1
	self.selList = MOHO.SelectedPointList(mesh)
	self.numSel = table.getn(self.selList)

	if (self.numSel < 2) then -- work with only the closest selection
		-- find the closest point here
		self.selID = mesh:ClosestPoint(mouseEvent.startVec)
		if (self.selID >= 0) then
			self.numSel = 1
			mesh:SelectNone()
			local pt = mesh:Point(self.selID)
			pt.fSelected = true
			if (pt.fWidth.value < 0) then
				pt.fWidth:SetValue(moho.frame, 0)
			end
			pt.fTempWidth = pt.fWidth.value
			moho:UpdateSelectedChannels()
		end
		self.selList = MOHO.SelectedPointList(mesh)
		self:UpdateWidgets(moho)
	end

	for i, pt in self.selList do
		if (pt.fWidth.value < 0) then
			pt.fWidth:SetValue(moho.frame, 0)
		end
		pt.fTempWidth = pt.fWidth.value
	end
	
	mouseEvent.view:DrawMe()
end

function LM_ScalePoints:OnMouseMoved(moho, mouseEvent)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	if (self.scalingDir < 0) then
		return
	end

	local scaling = LM.Vector2:new_local()

	scaling:Set(1, 1)
	-- scaling connected to actual drag amount
	local v1 = mouseEvent.startVec - self.centerVec
	local v2 = mouseEvent.vec - self.centerVec
	scaling.x = v2.x / v1.x
	scaling.y = v2.y / v1.y

	if (self.scalingDir == 0) then
		scaling.y = 1
		if (mouseEvent.altKey) then
			scaling.y = 1 / scaling.x
		end
	elseif (self.scalingDir == 1) then
		scaling.x = 1
		if (mouseEvent.altKey) then
			scaling.x = 1 / scaling.y
		end
	else
		scaling.x = (scaling.x + scaling.y) / 2
		scaling.y = scaling.x
	end

	mesh:ScalePoints(scaling.x, scaling.y, self.centerVec)
	moho:AddPointKeyframe(moho.frame)

	local offset = (math.abs(scaling.x) + math.abs(scaling.y))/2
	local maxWidth = moho:PixelToDoc(64)

	if (self.numSel == 1) then
		local pt = mesh:Point(self.selID)
		pt.fWidth.value = pt.fTempWidth * offset
		pt.fWidth.value = LM.Clamp(pt.fWidth.value, 0, maxWidth)
		pt.fWidth:StoreValue()
	else
		for i, pt in self.selList do
			pt.fWidth.value = pt.fTempWidth * offset
			pt.fWidth.value = LM.Clamp(pt.fWidth.value, 0, maxWidth)
			pt.fWidth:StoreValue()
		end
	end

	mouseEvent.view:DrawMe()
end

function LM_ScalePoints:OnMouseUp(moho, mouseEvent)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	if (self.scalingDir < 0) then
		return
	end
	
	if (self.numSel == 1) then
		-- update the selected list
		self.selList = MOHO.SelectedPointList(mesh)
	end
	-- clear out NULL width key values if there is more than one keyframe
	for i, pt in self.selList do
		if (pt.fWidth:CountKeys() > 1) then
			for id = pt.fWidth:CountKeys() - 1, 0, -1 do
				local when = pt.fWidth:GetKeyWhen(id)
				if (pt.fWidth:GetValue(when) < 0) then
					pt.fWidth:DeleteKey(when)
				end
			end
			if (pt.fWidth:CountKeys() < 1) then
				pt.fWidth:SetValue(0, -1)
			end
		end
	end
	
	self.selList = nil
	moho.layer:UpdateCurFrame()
	moho:NewKeyframe(CHANNEL_WIDTH)
	moho:UpdateSelectedChannels()

	moho:AddPointKeyframe(moho.frame)
	moho:NewKeyframe(CHANNEL_POINT)

	mouseEvent.view:DrawMe()
end

function LM_ScalePoints:OnKeyDown(moho, keyEvent)
	LM_SelectPoints:OnKeyDown(moho, keyEvent)
end

function LM_ScalePoints:DrawMe(moho, view)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	local g = view:Graphics()
	local min = LM.Vector2:new_local()
	local max = LM.Vector2:new_local()
	local markerR = 4
	local matrix = LM.Matrix:new_local()

	mesh:SelectedBounds(min, max)

	moho.layer:GetFullTransform(moho.frame, matrix, moho.document)
	g:Push()
	g:ApplyMatrix(matrix)

	g:SetColor(255, 0, 0)
	g:SetSmoothing(true)
	g:DrawLine(min.x, min.y, max.x, min.y)
	g:DrawLine(min.x, max.y, max.x, max.y)
	g:DrawLine(min.x, min.y, min.x, max.y)
	g:DrawLine(max.x, min.y, max.x, max.y)
	g:SetSmoothing(false)
	g:DrawFatMarker(min.x, min.y, markerR)
	g:DrawFatMarker(min.x, max.y, markerR)
	g:DrawFatMarker(max.x, min.y, markerR)
	g:DrawFatMarker(max.x, max.y, markerR)
	g:DrawFatMarker((min.x + max.x) / 2, min.y, markerR)
	g:DrawFatMarker((min.x + max.x) / 2, max.y, markerR)
	g:DrawFatMarker(min.x, (min.y + max.y) / 2, markerR)
	g:DrawFatMarker(max.x, (min.y + max.y) / 2, markerR)

	g:Pop()
end

-- **************************************************
-- Tool options - create and respond to tool's UI
-- **************************************************

LM_ScalePoints.DUMMY = MOHO.MSG_BASE
LM_ScalePoints.MODIFY = MOHO.MSG_BASE + 1
LM_ScalePoints.RESET = MOHO.MSG_BASE + 2
LM_ScalePoints.SELECTITEM = MOHO.MSG_BASE + 3

function LM_ScalePoints:DoLayout(moho, layout)
	self.menu = LM.GUI.Menu(MOHO.Localize(self.BASE_STR + 2, "Select Group"))

	self.popup = LM.GUI.PopupMenu(128, false)
	self.popup:SetMenu(self.menu)
	layout:AddChild(self.popup)

	layout:AddChild(LM.GUI.StaticText(MOHO.Localize(self.BASE_STR + 3, "Scale")))

	layout:AddChild(LM.GUI.StaticText(MOHO.Localize(MOHO.STR_X, "X:")))
	self.scaleX = LM.GUI.TextControl(0, "00.0000", 0, LM.GUI.FIELD_FLOAT)
	layout:AddChild(self.scaleX)

	layout:AddChild(LM.GUI.StaticText(MOHO.Localize(MOHO.STR_Y, "Y:")))
	self.scaleY = LM.GUI.TextControl(0, "00.0000", 0, LM.GUI.FIELD_FLOAT)
	layout:AddChild(self.scaleY)

	layout:AddChild(LM.GUI.Button(MOHO.Localize(self.BASE_STR + 4, "Modify"), self.MODIFY))
	layout:AddChild(LM.GUI.Button(MOHO.Localize(MOHO.STR_RESET, "Reset"), self.RESET))
end

function LM_ScalePoints:UpdateWidgets(moho)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	MOHO.BuildGroupMenu(self.menu, mesh, self.SELECTITEM, self.DUMMY)

	self.scaleX:SetValue(1)
	self.scaleY:SetValue(1)
end

function LM_ScalePoints:HandleMessage(moho, view, msg)
	local mesh = moho:Mesh()
	if (mesh == nil) then
		return
	end

	if (msg == self.MODIFY) then
		moho.document:PrepUndo(moho.layer)
		moho.document:SetDirty()

		local centerVec = mesh:SelectedCenter()
		local scaleX = self.scaleX:FloatValue()
		local scaleY = self.scaleY:FloatValue()

		mesh:PrepMovePoints()
		mesh:ScalePoints(scaleX, scaleY, centerVec)
		moho:AddPointKeyframe(moho.frame)
		moho:NewKeyframe(CHANNEL_POINT)
	elseif (msg == self.RESET) then
		if (moho.frame > 0) then
			moho.document:PrepUndo(moho.layer)
			moho.document:SetDirty()

			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				if (pt.fSelected) then
					pt:SetPos(pt.fAnimPos:GetValue(0), moho.frame)
				end
			end
		end
		moho:NewKeyframe(CHANNEL_POINT)
	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
Post Reply