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

ScriptName = "SS_CurveExposure"

-- **************************************************
-- Based on LM_CurveExposure 7.0
-- Updated by SimplSam #511022 for MH12+
--   add: <ctrl>-click to Set new Curve Start Point
-- **************************************************

SS_CurveExposure = {}

function SS_CurveExposure:Name()
	return "Stroke Exposure"
end

function SS_CurveExposure:Version()
	return "7.0+ #511022"
end

function SS_CurveExposure:Description()
	return ("Drag side to side to adjust the exposure of a stroke (hold <alt> to expose the opposite end of the stroke. <ctrl/cmd> Click point to set stroke start")
	--#SC return MOHO.Localize("/Scripts/Tool/CurveExposure/Description=Drag side to side to adjust the exposure of a stroke (hold <alt> to expose the opposite end of the stroke")
end

function SS_CurveExposure:Creator()
	return ("Lost Marble LLC  (+ SimplSam)")
end

function SS_CurveExposure:UILabel()
	return(MOHO.Localize("/Scripts/Tool/CurveExposure/StrokeExposure=Stroke Exposure"))
	end

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

SS_CurveExposure.selID = -1
SS_CurveExposure.savedVal = 0

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

function SS_CurveExposure:IsEnabled(moho)
	if (moho:CountCurves() > 0) then
		return true
	end
	return false
end

function SS_CurveExposure:IsRelevant(moho)
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return false
	end
	return true
end

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

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

	self.selID = -1

	if (not mouseEvent.ctrlKey) then --#SC Ignore Mouse actions if CTRL Key pressed
		local selCount = 0
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected()) then
				selCount = selCount + 1
			end
		end
		if (selCount < 2) then
			local curveID = -1
			local segID = -1
			curveID, segID = mouseEvent.view:PickEdge(mouseEvent.pt, curveID, segID)
			if (curveID >= 0) then
				mesh:SelectNone()
				self.selID = curveID
				local curve = mesh:Curve(self.selID)
				for i = 0, curve:CountPoints() - 1 do
					curve:Point(i).fSelected = true
				end
			end
		end
	end

	self.savedVal = 0
	mouseEvent.view:DrawMe()
end

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

	local newVal = (mouseEvent.pt.x - mouseEvent.startPt.x) / mouseEvent.view:Graphics():Width()
	local curve = nil

	for i = 0, mesh:CountCurves() - 1 do
		local curve = mesh:Curve(i)
		if (curve:IsSelected()) then
			local exp = 0
			if (mouseEvent.altKey) then
				exp = curve.fStartPercent.value
			else
				exp = curve.fEndPercent.value
			end
			exp = exp - self.savedVal + newVal
			exp = LM.Clamp(exp, -0.01, 1.01)
			if (mouseEvent.altKey) then
				curve.fStartPercent:SetValue(moho.drawingLayerFrame, exp)
			else
				curve.fEndPercent:SetValue(moho.drawingLayerFrame, exp)
			end
		end
	end

	self.savedVal = newVal
	moho.drawingLayer:UpdateCurFrame()
	mouseEvent.view:DrawMe()
end

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

	--#SC If ctrl-click - Set single selected point on outlined closed curve as start, else select all connected points
	if (mouseEvent.ctrlKey and not mouseEvent.shiftKey) then
        local didSetStartPoint = false
		LM_SelectPoints:OnMouseUp(moho, mouseEvent)
		if (moho:CountSelectedPoints() == 1) then
			for iCurve = 0, mesh:CountCurves() - 1 do
				local curve = mesh:Curve(iCurve)
				if (curve:IsPartiallySelected() and curve.fClosed) then
					local curvePointCount = curve:CountPoints()
					if (curvePointCount > 2) then
						for iPoint = 1, curvePointCount - 1 do
							local pt = curve:Point(iPoint)
							if (pt.fSelected) then
                                for iShape = 0, mesh:CountShapes() -1 do
                                    local shape = mesh:Shape(iShape)
                                    if (shape:ContainsEdge(iCurve, iPoint -1)) then
                                        if (shape.fHasOutline) then
                                            local fillAllowed = shape.fFillAllowed
                                            local hasFill = shape.fHasFill
                                            mesh:AddPoint(pt.fPos, iCurve, iPoint -1, 0)
                                            mesh:DeleteEdge(iCurve, iPoint, 0)
                                            mesh:WeldPoints(mesh:PointID(curve:Point(curvePointCount)), mesh:PointID(pt), 0)
                                            shape.fFillAllowed = fillAllowed
                                            shape.fHasFill = hasFill
                                            didSetStartPoint = true
                                        end
                                        break
                                    end
                                end
								break
							end
						end
					end
					break
				end
			end
		end
		if (not didSetStartPoint) then
            mesh:SelectConnected()
		end
	end

	moho:UpdateSelectedChannels()
	moho:NewKeyframe(CHANNEL_CURVEEXP)
end

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

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

SS_CurveExposure.CHANGE_START = MOHO.MSG_BASE + 1
SS_CurveExposure.CHANGE_END = MOHO.MSG_BASE + 2
SS_CurveExposure.DUMMY = MOHO.MSG_BASE + 3
SS_CurveExposure.SELECTITEM = MOHO.MSG_BASE + 4

function SS_CurveExposure:DoLayout(moho, layout)
	self.menu = LM.GUI.Menu(MOHO.Localize("/Scripts/Tool/CurveExposure/SelectGroup=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("/Scripts/Tool/FollowCurve/StartPercentage=Start percentage:")))
	self.startPercentage = LM.GUI.TextControl(0, "00.0000", self.CHANGE_START, LM.GUI.FIELD_UFLOAT)
	self.startPercentage:SetWheelInc(1.0)
	layout:AddChild(self.startPercentage)

	layout:AddChild(LM.GUI.StaticText(MOHO.Localize("/Scripts/Tool/FollowCurve/EndPercentage=End percentage:")))
	self.endPercentage = LM.GUI.TextControl(0, "00.0000", self.CHANGE_END, LM.GUI.FIELD_UFLOAT)
	self.endPercentage:SetWheelInc(1.0)
	layout:AddChild(self.endPercentage)
end

function SS_CurveExposure:UpdateWidgets(moho)
	if (moho:CurrentTool() ~= "SS_CurveExposure") then
		return -- this could get called when doing a double-tap on a multitouch Wacom device with a different active tool
	end

	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end

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

	local numCurves = 0
	local startPercent = 0.0
	local endPercent = 0.0
	for i = 0, mesh:CountCurves() - 1 do
		local curve = mesh:Curve(i)
		if (curve:IsSelected()) then
			numCurves = numCurves + 1
			startPercent = startPercent + curve.fStartPercent.value
			endPercent = endPercent + curve.fEndPercent.value
		end
	end
	if (numCurves > 0) then
		startPercent = startPercent * 100.0 / numCurves
		startPercent = LM.Clamp(startPercent, 0.0, 100.0)
		endPercent = endPercent * 100.0 / numCurves
		endPercent = LM.Clamp(endPercent, 0.0, 100.0)
		self.startPercentage:SetValue(startPercent)
		self.startPercentage:Enable(true)
		self.endPercentage:SetValue(endPercent)
		self.endPercentage:Enable(true)
	else
		self.startPercentage:SetValue("")
		self.startPercentage:Enable(false)
		self.endPercentage:SetValue("")
		self.endPercentage:Enable(false)
	end
end

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

	if (msg == self.CHANGE_START) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local startPercent = self.startPercentage:FloatValue() / 100.0
		startPercent = LM.Clamp(startPercent, 0.0, 1.0)
		if (startPercent < 0.0001) then
			startPercent = -0.01
		end
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected()) then
				curve.fStartPercent:SetValue(moho.drawingLayerFrame, startPercent)
			end
		end
		view:DrawMe()
		moho:NewKeyframe(CHANNEL_CURVEEXP)
		moho:UpdateUI()
	elseif (msg == self.CHANGE_END) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local endPercent = self.endPercentage:FloatValue() / 100.0
		endPercent = LM.Clamp(endPercent, 0.0, 1.0)
		if (endPercent > 0.9999) then
			endPercent = 1.01
		end
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected()) then
				curve.fEndPercent:SetValue(moho.drawingLayerFrame, endPercent)
			end
		end
		view:DrawMe()
		moho:NewKeyframe(CHANNEL_CURVEEXP)
		moho:UpdateUI()
	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

Icon
Curve Exposure+ Change Anchor Point of Curve
Listed

Script type: Tool

Uploaded: Oct 23 2021, 20:48

Allows the Anchor Point on an exposure curve to be set to any Point of the curve
A quick hack of the standard Curve Exposure tool. The update allows you to Ctrl/Cmd-Click a Point on a curve and have that point set as the start anchor point of the Exposure Curve.

How to use:

- Select the SS Curve Exposure tool
- Ctrl-Click on the desired start point

Image

If the new-start-point change works - then the point will be the only point selected. Click or Ctrl-Click that point again to select all connected points.

Notes:

For the new feature to work successfully...

The Shape Curve of the selected point must:
- be Stroked/Outlined
- be Closed
- have more than 2 points

The selected Point must not:
- already be the first point

If the point is not set-able or already set as the start of the curve, then all connected points will be selected. This is similar to the selection behaviour of the standard tool, and also allows you to Ctrl double-Click a point to make it the Start exposure point and then permit the exposure function to work as normal.

Behind the scenes it works pretty much in a manner illustrated by @EricTheFish on the LostMarble Forum. i.e. Add a new point, Delete Edge, Weld points and Fix the Shape.
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: 74