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

ScriptName = "LK_SelectPoints"

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

LK_SelectPoints = {}

function LK_SelectPoints:Name()
	return "Select Points"
end

function LK_SelectPoints:Version()
	return "6.4"
end

function LK_SelectPoints:IsBeginnerScript()
	return true
end

function LK_SelectPoints:Description()
	local ctrlkeyName = "ctrl"
	local altkeyName = "alt"
	if (FO_Utilities:getOS() == "unix") then
		ctrlkeyName = "cmd"
		altkeyName = "option"
	end
	return "Select/deselect points (hold <shift> to add to selection, <"..altkeyName.."> to remove from selection, <"..ctrlkeyName.."> to select curve, <,/.> to go to previous/next FBF-frame, </> to fill point-selection, hold <"..ctrlkeyName.."/"..altkeyName.."/shift> to add point-group associated with clicked point to selection), <0> to <9> to select points by color, <"..altkeyName.." + 0> to <"..altkeyName.." + 9> to set selected points color"
end

function LK_SelectPoints:BeginnerDescription()
	return MOHO.Localize("/Scripts/Tool/SelectPoints/BeginnerDescription=To select points, drag a rectangle around them. You can also click on a single point to select it. Click on a curve to select the entire curve or click on a filled-in area to select an entire object.")
end

function LK_SelectPoints:BeginnerDisabledDescription()
	return MOHO.Localize("/Scripts/Tool/SelectPoints/BeginnerDisabledDescription=A vector layer needs to be selected in the Layers window to use this tool.")
end

function LK_SelectPoints:Creator()
	return "Smith Micro Software, Inc. modded by Lukas Krepel, Frame Order"
end

function LK_SelectPoints:UILabel()
	return MOHO.Localize("/Scripts/Tool/SelectPoints/SelectPoints=Select Points")
end

function LK_SelectPoints:LoadPrefs(prefs)
	self.lassoMode = prefs:GetBool("LK_SelectPoints.lassoMode", true)
	self.simplifyValue = prefs:GetInt("LK_SelectPoints.simplifyValue", 25)
end

function LK_SelectPoints:SavePrefs(prefs)
	prefs:SetBool("LK_SelectPoints.lassoMode", self.lassoMode)
	prefs:SetInt("LK_SelectPoints.simplifyValue", self.simplifyValue)
end

function LK_SelectPoints:ResetPrefs()
	self.lassoMode = true
	self.simplifyValue = 25
end

function  LK_SelectPoints:ColorizeIcon()
	return true
end

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

LK_SelectPoints.SELMODE_GROUP_PTS = 0
LK_SelectPoints.SELMODE_PT = 1
LK_SelectPoints.SELMODE_EDGE = 2
LK_SelectPoints.SELMODE_SHAPE = 3
LK_SelectPoints.selMode = 0 -- SELMODE_GROUP_PTS
LK_SelectPoints.selRect = LM.Rect:new_local()
LK_SelectPoints.previousX = 0
LK_SelectPoints.previousY = 0
LK_SelectPoints.allowShapePicking = true
LK_SelectPoints.ctrlKeySelection = false
LK_SelectPoints.isMouseDragging = false
LK_SelectPoints.simplifyValue = 25
LK_SelectPoints.undoPrepped = false

LK_SelectPoints.shortcutColor = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "--9", "--10", "--11", "0" }

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

function LK_SelectPoints:IsEnabled(moho)
	if (moho.drawingLayer:LayerType() ~= MOHO.LT_VECTOR) then
		return false
	else
		return true
	end
end

function LK_SelectPoints:IsRelevant(moho)
	if MohoMode ~= nil then
		if MohoMode.vanilla then
			return false
		end
	end
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return false
	end
	return true
end

-- **************************************************
-- Replace original
-- **************************************************
function LM_SelectPoints:IsRelevant(moho)
	if LK_SelectPoints:IsRelevant(moho) then
		return false
	end
	-- * Copy of original function:
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return false
	end
	return true
end

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

	self.isMouseDragging = true
	self.ctrlKeySelection = false
	self.oldLassoMode = self.lassoMode
	if mouseEvent.ctrlKey then	
		self.lassoMode = not self.lassoMode
	end
	self.pointGroupMode = false
	if (mouseEvent.ctrlKey and mouseEvent.shiftKey and mouseEvent.altKey) then
		self.pointGroupMode = true
	end
	if mouseEvent.ctrlKey then
		LK_SelectPoints.ctrlKeySelection = true
	else
		LK_SelectPoints.ctrlKeySelection = false
	end

	self.selMode = self.SELMODE_GROUP_PTS
	if (not mouseEvent.shiftKey and not mouseEvent.altKey) then
		mesh:SelectNone()
	end

	if (self.selMode == self.SELMODE_GROUP_PTS) then
		if (self.lassoMode) then
			self.lassoList = { { mouseEvent.startPt.x, mouseEvent.startPt.y } }
			self.previousX = mouseEvent.startPt.x
			self.previousY = mouseEvent.startPt.y
		else
			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)
		end
	end
	mouseEvent.view:DrawMe()
end

function LK_SelectPoints:OnMouseMoved(moho, mouseEvent)
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end
	if (self.selMode == self.SELMODE_GROUP_PTS) then
		if self.lassoMode then
			local g = mouseEvent.view:Graphics()

			g:SetSmoothing(true)
			g:Push()
			local m = g:CurrentTransform()
			m:Invert()
			g:ApplyMatrix(m)
			g:SetColor(MOHO.MohoGlobals.SelCol)
			g:MoveTo(self.previousX, self.previousY)
			g:LineTo(mouseEvent.pt.x, mouseEvent.pt.y)
			g:Pop()
			g:SetSmoothing(false)
			mouseEvent.view:RefreshView()

			table.insert(self.lassoList, { mouseEvent.pt.x, mouseEvent.pt.y })
			self.previousX = mouseEvent.pt.x
			self.previousY = mouseEvent.pt.y
		else
			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()
		end
		mouseEvent.view:DrawMe()
	end
end

function LK_SelectPoints:OnMouseUp(moho, mouseEvent)
	self.allowShapePicking = true
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end
	local mouseDist = math.abs(mouseEvent.pt.x - mouseEvent.startPt.x) + math.abs(mouseEvent.pt.y - mouseEvent.startPt.y)
	if (mouseDist < 8) then
		-- Single-click selection - select a shape or curve under the mouse
		self:SingleClickSelect(moho, mouseEvent, false, true)
	elseif (self.selMode == self.SELMODE_GROUP_PTS and self.lassoMode) then
		-- draw the finalized lasso outline
		local g = mouseEvent.view:Graphics()

		g:SetSmoothing(true)
		g:Push()
		local m = g:CurrentTransform()
		m:Invert()
		g:ApplyMatrix(m)
		g:SetColor(MOHO.MohoGlobals.SelCol)
		g:MoveTo(self.previousX, self.previousY)
		g:LineTo(mouseEvent.startPt.x, mouseEvent.startPt.y)
		g:Pop()
		g:SetSmoothing(false)
		-- mouseEvent.view:RefreshView()
		-- LM.Snooze(100)
	end

	self.isMouseDragging = false

	if (self.selMode == self.SELMODE_GROUP_PTS and mesh:CountPoints() > 0) then
		if self.lassoMode then
			local g = mouseEvent.view:Graphics()

			-- draw the lasso shape, and do hit testing
			-- 1 - draw the lasso shape
			local end1 = LM.Vector2:new_local()
			local end2 = LM.Vector2:new_local()
			g:Clear(0, 0, 0, 0)
			g:Push()
			local m = g:CurrentTransform()
			m:Invert()
			g:ApplyMatrix(m)
			g:SetColor(255, 255, 255, 255)
			g:BeginShape()
			for i = 1, #self.lassoList - 1 do
				end1:Set(self.lassoList[i][1], self.lassoList[i][2])
				end2:Set(self.lassoList[i + 1][1], self.lassoList[i + 1][2])
				g:AddLine(end1, end2)
			end
			end1:Set(self.lassoList[#self.lassoList][1], self.lassoList[#self.lassoList][2])
			end2:Set(self.lassoList[1][1], self.lassoList[1][2])
			g:AddLine(end1, end2)
			g:EndShape()
			g:Pop()
			-- test code to view the lasso's shape
			-- mouseEvent.view:RefreshView()
			-- LM.Snooze(1000)
			-- 2 - do hit testing on the lasso shape
			local v = LM.Vector2:new_local()
			local screenPt = LM.Point:new_local()
			local m = LM.Matrix:new_local()

			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)
					g:WorldToScreen(v, screenPt)
					if (g:IsFullWhite(screenPt)) then
						if (mouseEvent.altKey) then
							pt.fSelected = false
						else
							pt.fSelected = true
						end
					end
				end
			end
		else
			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
		end
	end
	self.lassoList = nil
	self.lassoMode = self.oldLassoMode -- Reverting in case it changed while holding Ctrl during OnMouseDown
	FO_Utilities:PointsColorKeys(moho)
	moho:UpdateSelectedChannels()
end

function LK_SelectPoints:OnKeyDown(moho, keyEvent)
	-- ***********************************
	-- *** Numeric keys 1 t/m 6 and 0. ***
	-- ***********************************
	for i = 0, #self.shortcutColor do
		if self.shortcutColor[i] == keyEvent.key then
			if not keyEvent.altKey then
				self:HandleMessage(moho, keyEvent.view, self.SELECT_POINT_COLOR + i - 1) -- * NO MODIFIERS
			else
				self:HandleMessage(moho, keyEvent.view, self.SET_POINT_COLOR + i - 1) -- * ALT
			end
		end
	end
	--
	if keyEvent.key == "." then
		FO_Utilities:GoToNextSwitchKey(moho, false)
		return
	elseif keyEvent.key == "," then
		FO_Utilities:GoToNextSwitchKey(moho, true)
		return
	end

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

	if ((keyEvent.keyCode == LM.GUI.KEY_DELETE) or (keyEvent.keyCode == LM.GUI.KEY_BACKSPACE)) then
		if (not keyEvent.view:IsMouseDragging()) then
			moho.document:PrepUndo(moho.drawingLayer)
			moho.document:SetDirty()
			MOHO.DeleteSelectedPoints(mesh)
			keyEvent.view:DrawMe()
		end
	end
	--
	if keyEvent.key == "/" then
		self:HandleMessage(moho, keyEvent.view, self.FILL_POINTS)
	end
end

function LK_SelectPoints:DrawMe(moho, view)
	local mohoVersion = FO_Utilities.MohoVersion(moho)
	-- *
	local markerSize = 3
	local markerOutlineThickness = 1
	local selectedMarkerOutlineThickness = 2
	-- * Temp solution because Moho 14 doesn't draw FatMarkers correctly:
	if mohoVersion >= 14 then
		markerSize = markerSize + 1
		selectedMarkerOutlineThickness = selectedMarkerOutlineThickness + 1 -- * Diamand markers are different
		markerOutlineThickness = markerOutlineThickness 
	end
	-- *
	local mesh = moho:DrawingMesh()
	if MOHO.hasbit(view:QualityFlags(), MOHO.bit(MOHO.LDQ_WIREFRAME)) then
		if mesh ~= nil then
			local g = view:Graphics()
			local layerMatrix = LM.Matrix:new_local()
			moho.layer:GetFullTransform(moho.frame, layerMatrix, moho.document)
			g:Push()
			g:ApplyMatrix(layerMatrix)
			for i = 0, mesh:CountGroups()-1 do
				local group = mesh:Group(i)
				for j = 0, group:CountPoints()-1 do
					local groupName = group:Name()
					if groupName ~= "Shy" then
						local color = 0
						for c = 1, #FO_Utilities.colorNames do
							local colorName = FO_Utilities.colorNames[c]
							if string.lower(groupName) == string.lower(colorName) then
								color = c
							end
						end
						local point = group:Point(j)
						if not point.fHidden then
							if point.fSelected then
								g:SetColor(MOHO.MohoGlobals.SelCol)
								if mohoVersion >= 14 then -- * Temp solution because Moho 14 doesn't draw FatMarkers correctly:
									g:DrawDiamondMarker(point.fPos.x, point.fPos.y, markerSize+selectedMarkerOutlineThickness)
								else
									g:DrawFatMarker(point.fPos.x, point.fPos.y, markerSize+selectedMarkerOutlineThickness)
								end
							else
								g:SetColor(MOHO.MohoGlobals.ElemCol)
								if mohoVersion >= 14 then -- * Temp solution because Moho 14 doesn't draw FatMarkers correctly:
									g:DrawDiamondMarker(point.fPos.x, point.fPos.y, markerSize+markerOutlineThickness)
								else
									g:DrawFatMarker(point.fPos.x, point.fPos.y, markerSize+markerOutlineThickness)
								end
							end
							g:SetColor(FO_Utilities.colorsR[color], FO_Utilities.colorsG[color], FO_Utilities.colorsB[color])
							if mohoVersion >= 14 then -- * Temp solution because Moho 14 doesn't draw FatMarkers correctly:
								g:DrawDiamondMarker(point.fPos.x, point.fPos.y, markerSize)
							else
								g:DrawFatMarker(point.fPos.x, point.fPos.y, markerSize)
							end
						end
					end
				end
			end
		end
	end
	if (self.isMouseDragging and self.selMode == self.SELMODE_GROUP_PTS) then
		local g = view:Graphics()
		if self.lassoMode then
			g:SetSmoothing(true)
			g:Push()
			local m = g:CurrentTransform()
			m:Invert()
			g:ApplyMatrix(m)
			g:SetColor(MOHO.MohoGlobals.SelCol)
			g:MoveTo(self.lassoList[1][1], self.lassoList[1][2])
			for i = 2, #self.lassoList do
				g:LineTo(self.lassoList[i][1], self.lassoList[i][2])
			end
			g:Pop()
			g:SetSmoothing(false)
		else
			g:SelectionRect(self.selRect)
		end
	end
end

function LK_SelectPoints:OnInputDeviceEvent(moho, deviceEvent)
	return LM_TransformPoints:OnInputDeviceEvent(moho, deviceEvent)
end

function LK_SelectPoints:SingleClickSelect(moho, mouseEvent, tryToPreserveSelection, allowEmptySelection)
	-- if tryToPreserveSelection is true, then don't change the selection if some points will remain selected
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end

	if (tryToPreserveSelection and moho:CountSelectedPoints() < 2) then
		mesh:SelectNone()
		moho:CountSelectedPoints(true)
	end
	if (not tryToPreserveSelection and not mouseEvent.shiftKey and not mouseEvent.altKey) then
		mesh:SelectNone()
	end

	local i = mouseEvent.view:PickPoint(mouseEvent.pt)	
	if self.pointGroupMode then
		self:SelectGroupBySinglePoint(moho, mesh, i)
		return
	end

	if LK_SelectPoints.ctrlKeySelection then
		i = -1
	end--todo
	if (i >= 0) then --pick a point
		self.selMode = self.SELMODE_PT
		if (tryToPreserveSelection) then
			if (not mesh:Point(i).fSelected) then
				mesh:SelectNone()
			end
		end
		if (mouseEvent.altKey) then
			mesh:Point(i).fSelected = false
		else
			mesh:Point(i).fSelected = true
		end
	else
		if (tryToPreserveSelection) then
			for i = 0, mesh:CountPoints() - 1 do
				local point = mesh:Point(i)
				point.fPrevSelected = point.fSelected
				point.fSelected = false
			end
		end
		local curveID = -1
		local segID = -1
		curveID, segID = mouseEvent.view:PickEdge(mouseEvent.pt, curveID, segID)
		if (curveID >= 0 and segID >= 0) then -- an edge was clicked on
			self.selMode = SELMODE_EDGE
			local curve = mesh:Curve(curveID)

			-- new method - select the points in the curve that was clicked on
			if (mouseEvent.altKey) then
				local isCurveSelected = true
				for i = 0, curve:CountPoints() - 1 do
					if (not curve:Point(i).fSelected) then
						isCurveSelected = false
						break
					end
				end
				for i = 0, curve:CountPoints() - 1 do
					local point = curve:Point(i)
					if ((not isCurveSelected) or (point:CountCurves() < 2)) then
						point.fSelected = false
						if (point.fHidden) then
							point.fSelected = false
						end
					end
				end
			else
				for i = 0, curve:CountPoints() - 1 do
					local point = curve:Point(i)
					if (not point.fHidden) then
						point.fSelected = true
					end
				end
			end

			-- old method - select all points connected to the curve that was clicked on
--[[
			-- take current selection and mark it special
			for i = 0, mesh:CountPoints() - 1 do
				local point = mesh:Point(i)
				if (point.fSelected) then
					point.fSelected = false
					point.fPrevSelected = true
				end
			end
			-- select everything connected to the picked edge
			local curve = mesh:Curve(curveID)
			curve:Point(0).fSelected = true
			mesh:SelectConnected()
			-- turn the original selection back on as well
			for i = 0, mesh:CountPoints() - 1 do
				local point = mesh:Point(i)
				if (point.fPrevSelected) then
					point.fSelected = true
					point.fPrevSelected = false
				end
			end
]]
		elseif (self.allowShapePicking) then -- try to pick a shape
			local shapeID = mouseEvent.view:PickShape(mouseEvent.pt)
			if (shapeID >= 0) then
				self.selMode = SELMODE_SHAPE
--[[				-- take current selection and mark it special
				for i = 0, mesh:CountPoints() - 1 do
					local point = mesh:Point(i)
					if (point.fSelected) then
						point.fSelected = false
						point.fPrevSelected = true
					end
				end]]
				-- select (or deselect) everything connected to the shape
				local shape = mesh:Shape(shapeID)
				for i = 0, shape:CountEdges() - 1 do
					curveID, segID = shape:GetEdge(i, curveID, segID)
					local curve = mesh:Curve(curveID)
					if (mouseEvent.altKey) then
						curve:Point(segID).fSelected = false
						curve:Point(segID + 1).fSelected = false
					else
						curve:Point(segID).fSelected = true
						curve:Point(segID + 1).fSelected = true
					end
				end
--				mesh:SelectConnected()
--[[				-- turn the original selection back on as well
				for i = 0, mesh:CountPoints() - 1 do
					local point = mesh:Point(i)
					if (point.fPrevSelected) then
						point.fSelected = true
						point.fPrevSelected = false
					end
				end]]
			end
		end

		if (not allowEmptySelection) then
			local numSel = moho:CountSelectedPoints(true)
			if (numSel < 1) then
				for i = 0, mesh:CountPoints() - 1 do
					local point = mesh:Point(i)
					point.fSelected = point.fPrevSelected
					point.fPrevSelected = false
				end
			end
		end

		if (tryToPreserveSelection) then
			local preserveSelection = false
			-- pass 1 - check if any of the selection is still selected
			for i = 0, mesh:CountPoints() - 1 do
				local point = mesh:Point(i)
				if (point.fPrevSelected and point.fSelected) then
					preserveSelection = true
				end
			end
			-- pass 2 - preserve the selection
			if (preserveSelection) then
				for i = 0, mesh:CountPoints() - 1 do
					local point = mesh:Point(i)
					point.fSelected = point.fPrevSelected
					point.fPrevSelected = false
				end
			else
				for i = 0, mesh:CountPoints() - 1 do
					local point = mesh:Point(i)
					point.fPrevSelected = false
				end
			end
		end
	end
	moho:UpdateSelectedChannels()
end

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

LK_SelectPoints.CREATE =						MOHO.MSG_BASE
LK_SelectPoints.DELETE =						MOHO.MSG_BASE + 1
LK_SelectPoints.LASSO =							MOHO.MSG_BASE + 2
LK_SelectPoints.FLIP_H =						MOHO.MSG_BASE + 3
LK_SelectPoints.FLIP_V =						MOHO.MSG_BASE + 4
LK_SelectPoints.DUMMY =							MOHO.MSG_BASE + 5
LK_SelectPoints.WELDOVERLAPS =					MOHO.MSG_BASE + 6
LK_SelectPoints.SIMPLIFYCURVE =					MOHO.MSG_BASE + 7
LK_SelectPoints.CHANGE_SIMPLIFY =				MOHO.MSG_BASE + 8
LK_SelectPoints.SPLIT =							MOHO.MSG_BASE + 9
LK_SelectPoints.SLINK_X =						MOHO.MSG_BASE + 10
LK_SelectPoints.SLINK_Y =						MOHO.MSG_BASE + 11
LK_SelectPoints.SLINK =							MOHO.MSG_BASE + 12
LK_SelectPoints.CURVE_PEAK =					MOHO.MSG_BASE + 13
LK_SelectPoints.CURVE_SMOOTH =					MOHO.MSG_BASE + 14
LK_SelectPoints.SELECT_CONNECTED =				MOHO.MSG_BASE + 15
LK_SelectPoints.WELD_ENDS =						MOHO.MSG_BASE + 16
LK_SelectPoints.FILL_POINTS =					MOHO.MSG_BASE + 17
LK_SelectPoints.TOGGLE_SELECTED_POINTS_SHY =	MOHO.MSG_BASE + 18
LK_SelectPoints.HIDE_SHOW_SHY_POINTS =			MOHO.MSG_BASE + 19
LK_SelectPoints.HIDE_SELECTED_POINTS = 			MOHO.MSG_BASE + 20
LK_SelectPoints.SHOW_ALL_POINTS = 				MOHO.MSG_BASE + 21
LK_SelectPoints.SELECT_SIMILAR = 				MOHO.MSG_BASE + 22

LK_SelectPoints.SET_POINT_COLOR		=			MOHO.MSG_BASE + 50 -- * up to 61 (50 = 0 = Plain to 61 = 11 = Coral)
LK_SelectPoints.SELECT_POINT_COLOR =			MOHO.MSG_BASE + 62 -- * up to 43 (62 = 0 = Plain to 73 = 11 = Coral)
LK_SelectPoints.SELECTITEM =					MOHO.MSG_BASE + 100 -- * Has to be the last message.



LK_SelectPoints.lassoMode = true
LK_SelectPoints.oldLassoMode = true

function LK_SelectPoints:DoLayout(moho, layout)
	FO_Utilities.tinyUI = true
	local ctrlkeyName = "ctrl"
	local altkeyName = "alt"
	if (FO_Utilities:getOS() == "unix") then
		ctrlkeyName = "cmd"
		altkeyName = "option"
	end

	FO_Utilities:Divider(layout, nil, true)
	self.lassoCheck = LM.GUI.ImageButton("ScriptResources/FO_icons/Lasso", "Lasso", true, self.LASSO, true)
	layout:AddChild(self.lassoCheck)

	FO_Utilities:Divider(layout)
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.menu = LM.GUI.Menu("Select")
	self.popup = LM.GUI.PopupMenu(64, false)
	self.popup:SetMenu(self.menu)
	layout:AddChild(self.popup)
	self.groupName = LM.GUI.TextControl(0, "Room For a Big Long Name", 0, LM.GUI.FIELD_TEXT)
	self.groupName:SetValue("")
	layout:AddChild(self.groupName)
	layout:AddChild(LM.GUI.Button(MOHO.Localize("/Scripts/Tool/SelectPoints/Create=Create"), self.CREATE))
	layout:AddChild(LM.GUI.ImageButton("ScriptResources/FO_icons/trashcan", "Delete point group", false, self.DELETE, true))
	layout:Pop()

	FO_Utilities:Divider(layout)
	self.weldBut = LM.GUI.Button("Weld", self.WELDOVERLAPS)
	self.weldBut:SetToolTip(MOHO.Localize("/Scripts/Tool/SelectPoints/WeldSelectedCurvesWhereTheyCrossOtherCurves=Weld selected curves where they cross other curves"))
	layout:AddChild(self.weldBut)

	FO_Utilities:Divider(layout)
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.simplifyBut = LM.GUI.Button("Simplify", self.SIMPLIFYCURVE)
	self.simplifyBut:SetToolTip(MOHO.Localize("/Scripts/Tool/SelectPoints/SimplifySelectedCurves=Simplify selected curves"))
	layout:AddChild(self.simplifyBut)
	self.simplifyText = LM.GUI.TextControl(0, "0000", self.CHANGE_SIMPLIFY, LM.GUI.FIELD_UINT)
	layout:AddChild(self.simplifyText)
	layout:Pop()

	FO_Utilities:Divider(layout)
	self.splitBut = LM.GUI.ImageButton("ScriptResources/FO_icons/points_split_edge", "Split edge", false, self.SPLIT, true)
	self.splitBut:SetToolTip(MOHO.Localize("/Scripts/Tool/SelectPoints/AddAPointInTheMiddleOfSelectedEdges=Add a point in the middle of selected edges"))
	layout:AddChild(self.splitBut)

	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.flipH = LM.GUI.ImageButton("ScriptResources/FO_icons/points_flip_x", MOHO.Localize("/Scripts/Tool/SelectPoints/FlipH=Flip Horizontally"), false, self.FLIP_H, true)
	layout:AddChild(self.flipH)
	self.flipV = LM.GUI.ImageButton("ScriptResources/FO_icons/points_flip_y", MOHO.Localize("/Scripts/Tool/SelectPoints/FlipV=Flip Vertically"), false, self.FLIP_V, true)
	layout:AddChild(self.flipV)
	layout:Pop()

	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.slinkXButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_slink_points_x", "Slink points horizontally", false, self.SLINK_X, true)
	self.slinkYButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_slink_points_y", "Slink points vertically", false, self.SLINK_Y, true)
	self.slinkButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_slink_points", "Slink points both horizontally and vertically", false, self.SLINK, true)
	layout:AddChild(self.slinkXButton)
	layout:AddChild(self.slinkYButton)
	layout:AddChild(self.slinkButton)
	layout:Pop()

	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.curvePeakButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_curve_peak", "Set curvature to peak", false, self.CURVE_PEAK, true)
	self.curveSmoothButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_curve_smooth", "Set curvature to smooth", false, self.CURVE_SMOOTH, true)
	layout:AddChild(self.curvePeakButton)
	layout:AddChild(self.curveSmoothButton)
	layout:Pop()

	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.weldEndsButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_Weld_Ends", "Weld Curve Ends", false, self.WELD_ENDS, true)
	layout:AddChild(self.weldEndsButton)
	self.fillPointsButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_Fill_Points", "Fill Points", false, self.FILL_POINTS, true)
	layout:AddChild(self.fillPointsButton)
	layout:Pop()

	-- ***************	
	-- *** Select: ***
	-- ***************
	FO_Utilities:Divider(layout, "Select")
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	-- * Select Connected Button:
	self.floodSelectButton = LM.GUI.ImageButton("ScriptResources/FO_icons/points_Flood_Select_Points", "Select Connected Points", false, self.SELECT_CONNECTED, true)
	layout:AddChild(self.floodSelectButton)
	-- * Select Similar Button:
	self.selectSimilarButton = LM.GUI.Button("Similar", self.SELECT_SIMILAR)
	self.selectSimilarButton:SetToolTip("Select points in the same group as any selected point.")
	layout:AddChild(self.selectSimilarButton)
	layout:Pop()

	-- **************	
	-- *** Color: ***
	-- **************
	FO_Utilities:Divider(layout, "Color")
	self.colorButtons = {}
	for i = 0, 11 do
		local colorName = FO_Utilities.colorNames[i]
		self.colButton = LM.GUI.ImageButton("ScriptResources/FO_icons/color_"..colorName, "Set point color to "..colorName.." (<"..altkeyName.."> to select all "..colorName.." points)", true, self.SET_POINT_COLOR + i, false) -- backup alt tooltip, "Select all "..colorName.." points (<"..altkeyName.."> to Set selected points color to "..colorName..")"
		self.colButton:SetAlternateMessage(self.SELECT_POINT_COLOR + i)
		table.insert(self.colorButtons, self.colButton)
	end
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	layout:AddChild(self.colorButtons[1])
	local reverse = FO_Utilities.reverseBoneColorButtons
	local increment = 1
	local start = 2
	local stop = #self.colorButtons
	if reverse then
		start, stop = stop, start
		increment = -1
	end
	for i = start, stop, increment do
		local buttonColor = i - 1
		layout:AddChild(self.colorButtons[i])
	end
	layout:Pop()
	-- ************	
	-- *** Shy: ***
	-- ************
	FO_Utilities:Divider(layout, "Shy Points")
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.shyButton = LM.GUI.ImageButton("ScriptResources/FO_icons/txt_shy", "Toggle selected points as 'Shy'", true, self.TOGGLE_SELECTED_POINTS_SHY, true)
	layout:AddChild(self.shyButton)
	-- layout:AddPadding(-15)
	self.hideShowShyPointsButton = LM.GUI.ImageButton("ScriptResources/FO_icons/txt_show_hide_shy", "Hide/Show Shy Points", false, self.HIDE_SHOW_SHY_POINTS, true)
	layout:AddChild(self.hideShowShyPointsButton)
	layout:Pop()
	-- *******************
	-- *** Visibility: ***
	-- *******************
	FO_Utilities:Divider(layout, "Visibility")
	layout:PushH(LM.GUI.ALIGN_CENTER, 1)
	self.hideSelectedButton = LM.GUI.Button("Hide", self.HIDE_SELECTED_POINTS)
	self.hideSelectedButton:SetToolTip("Hide selected points.")
	layout:AddChild(self.hideSelectedButton)
	self.showAllButton = LM.GUI.Button("Reveal", self.SHOW_ALL_POINTS)
	self.showAllButton:SetToolTip("Reveal all hidden (non-shy) points.")
	layout:AddChild(self.showAllButton)
	layout:Pop()
end

function LK_SelectPoints:UpdateWidgets(moho)
	if (moho:CurrentTool() ~= "LK_SelectPoints") 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
	-- C start
	for i, button in ipairs(self.colorButtons) do
		button:SetValue(false)
	end

	local shyGroupExists = false
	for i = 0, mesh:CountGroups()-1 do
		local group = mesh:Group(i)
		if group:Name() == "Shy" then
			shyGroupExists = true
		end
	end
	self.hideShowShyPointsButton:Enable(shyGroupExists)
	local anyColorpointSelected = false
	for i = 0, mesh:CountPoints()-1 do
		local point = mesh:Point(i)
		local pointID = i
		if (point.fSelected) then
			for i, button in ipairs(self.colorButtons) do
				local buttonColor = i - 1
				local colorName = FO_Utilities.colorNames[buttonColor]
				for i = 0, mesh:CountGroups()-1 do
					local group = mesh:Group(i)
					local groupName = group:Name()
					if groupName == colorName then
						if group:ContainsPointID(pointID) then
							button:SetValue(true)
							anyColorpointSelected = true
						end
					end
				end
			end
		end
	end
	-- C end
	MOHO.BuildGroupMenu(self.menu, mesh, self.SELECTITEM, self.DUMMY)

	self.lassoCheck:SetValue(self.lassoMode)
	self.simplifyText:SetValue(self.simplifyValue)
	-- * Update buttons that require multiple selected points:
	local multiplePoints
	if (moho:CountSelectedPoints() > 1) then
		multiplePoints = true
		self.simplifyBut:Enable(self.simplifyValue > 0)
	else
		multiplePoints = false
		self.simplifyBut:Enable(false)
	end
	self.weldBut:Enable(multiplePoints)
	self.splitBut:Enable(multiplePoints)
	self.flipH:Enable(multiplePoints)
	self.flipV:Enable(multiplePoints)	
	self.slinkXButton:Enable(multiplePoints)
	self.slinkYButton:Enable(multiplePoints)
	self.slinkButton:Enable(multiplePoints)
	-- * Update buttons that require at least 1 selected point:
	local atLeast1Point
	if (moho:CountSelectedPoints() > 0) then
		atLeast1Point = true
	else
		atLeast1Point = false
	end
	self.curvePeakButton:Enable(atLeast1Point)
	self.curveSmoothButton:Enable(atLeast1Point)
	self.floodSelectButton:Enable(atLeast1Point)
	self.fillPointsButton:Enable(atLeast1Point)

	self.weldEndsButton:Enable(self:AtLeast2EndpointsOn1Curve(moho))

	self.shyButton:Enable(atLeast1Point)

	local shyGroup
	local atLeast1Shy = false
	for i = 0, mesh:CountGroups()-1 do
		local group = mesh:Group(i)
		if group:Name() == "Shy" then
			shyGroup = group
			for i = 0, mesh:CountPoints()-1 do
				local point = mesh:Point(i)
				if point.fSelected then
					if shyGroup:ContainsPointID(i) then
						atLeast1Shy = true
						break
					end
				end
			end
		end
	end
	self.shyButton:SetValue(atLeast1Shy)

	-- * todo optimize, probably already looping trough points in this function
	local atLeast1Hidden = false
	for i = 0, mesh:CountPoints() - 1 do
		if shyGroup == nil or not shyGroup:ContainsPointID(i) then
			local pt = mesh:Point(i)
			if pt.fHidden then
				atLeast1Hidden = true
				break
			end
		end
	end
	self.showAllButton:Enable(atLeast1Hidden)
	-- self.hideSelectedButton:Enable(false)
	self.selectSimilarButton:Enable(anyColorpointSelected)
end

function LK_SelectPoints:SelectGroupBySinglePoint(moho, mesh, pt)
	-- * Remember selection:
	local startSelection = {}
	for i = 0, mesh:CountPoints() - 1 do
		local pt = mesh:Point(i)
		if (pt.fSelected) then
			table.insert(startSelection, pt)
		end
	end
	mesh:SelectNone()
	local selectedAny = false
	for i = 0, mesh:CountGroups()-1 do
		local group = mesh:Group(i)
		if group:Name() ~= "Shy" then
			if group:ContainsPointID(pt) then
				for i = 0, group:CountPoints()-1 do
					local point = group:Point(i)
					point.fSelected = true
					selectedAny = true
				end
			end
		end
	end
	if not selectedAny then
		-- * Revert to remembered selection:
		for i = 1, #startSelection do
			local point = startSelection[i]
			point.fSelected = true
		end	
	end
end

function LK_SelectPoints:HideShowShyPoints(moho)
	local mesh = moho:Mesh()
	local shyGroup
	for i = 0, mesh:CountGroups()-1 do
		local group = mesh:Group(i)
		if group:Name() == "Shy" then
			shyGroup = group
		end
	end
	if shyGroup == nil then
		return
	end
	hide = true
	for i = 0, shyGroup:CountPoints()-1 do
		shyPoint = shyGroup:Point(i)
		if shyPoint.fHidden then
			hide = false
			break
		end
	end
	-- * Hide shy:
	for i = 0, shyGroup:CountPoints()-1 do
		shyPoint = shyGroup:Point(i)
		shyPoint.fHidden = hide
	end
	-- * Update current frame so viewport updates correctly when using point layerscripts:
	moho.layer:UpdateCurFrame()
end

function LK_SelectPoints:AtLeast2EndpointsOn1Curve(moho)
	local mesh = moho:DrawingMesh()
	-- *** Loop trough all points:
	-- * Check endpoints:
	local endpoints = {}
	-- * Check closed curves:
	local curvesClosed = true
	for i = 0, mesh:CountPoints() - 1 do
		local pt = mesh:Point(i)
		if (pt.fSelected) then
			if pt:IsEndpoint() then
				table.insert(endpoints, pt)
			end
			if not pt:Curve(0, i).fClosed then
				curvesClosed = false
			end
		end
	end
	if (moho:CountSelectedPoints() == 0) then
		curvesClosed = false
	end
	-- * Check curves:
	local curves = {}
	if #endpoints >= 2 then
		for i = 1, #endpoints do
			local endpoint = endpoints[i]
			for i = 0, mesh:CountCurves() - 1 do
				local curve = mesh:Curve(i)
				local curveID = mesh:CurveID(curve)
				if endpoint:IsPointOnCurve(curveID) then
					if not table.contains(curves, curve) then
						table.insert(curves, curve) -- niet dubbel! todo
					end
				end
			end
		end
	end
	return (#endpoints >= 2 and #curves >= 1)
end

function LK_SelectPoints:ToggleSelectedPointsColor(moho, color)
	local mesh = moho:DrawingMesh()
	local colorName = FO_Utilities.colorNames[color]
	local colorGroup = nil
	local addPoint = false
	for i = 0, mesh:CountGroups()-1 do
		group = mesh:Group(i)
		if colorName == group:Name() then
			colorGroup = group
		end
	end
	if colorGroup == nil then
		addPoint = true
	else
		for i = 0, mesh:CountPoints()-1 do
			local point = mesh:Point(i)
			if point.fSelected then
				if not colorGroup:ContainsPointID(i) then
					addPoint = true
				end
			end
		end
	end
	-- * Remember selection:
	local startSelection = {}
	for i = 0, mesh:CountPoints() - 1 do
		local pt = mesh:Point(i)
		if (pt.fSelected) then
			table.insert(startSelection, pt)
		end
	end
	if addPoint then
		for i = 0, #FO_Utilities.colorNames do
			local otherColorName = FO_Utilities.colorNames[i]
			if otherColorName ~= colorName then
				self:RemoveSelectedPointsFromGroup(moho, otherColorName)
			end
		end
		mesh:SelectGroup(colorName)
		mesh:AddGroup(colorName)
	else
		self:RemoveSelectedPointsFromGroup(moho, colorName)--todo
	end
	-- * Revert to remembered selection:
	mesh:SelectNone()
	for i = 1, #startSelection do
		local point = startSelection[i]
		point.fSelected = true
	end	
	moho:UpdateUI()
end

function LK_SelectPoints:RemoveSelectedPointsFromGroup(moho, groupName)
	local mesh = moho:DrawingMesh()
	local targetGroup = nil
	for i = 0, mesh:CountGroups()-1 do
		group = mesh:Group(i)
		if groupName == group:Name() then
			targetGroup = group
			break
		end
	end
	if targetGroup == nil then
		return
	end
	local startSelection = {}
	for i = 0, mesh:CountPoints() - 1 do
		local pt = mesh:Point(i)
		if (pt.fSelected) then
			table.insert(startSelection, pt)
		end
	end
	local groupPoints = {}
	for i = 0, targetGroup:CountPoints()-1 do
		local point = targetGroup:Point(i)
		if not point.fSelected then
			local pointID = mesh:PointID(point)
			table.insert(groupPoints, pointID)
		end
	end
	mesh:DeleteGroup(groupName)
	for i = 0, mesh:CountPoints()-1 do
		local point = mesh:Point(i)
		if table.contains(groupPoints, i) then					
			point.fSelected = true
		else
			point.fSelected = false
		end
	end
	mesh:AddGroup(groupName)
	mesh:SelectNone()
	for i = 1, #startSelection do
		local point = startSelection[i]
		point.fSelected = true
	end
end

function LK_SelectPoints:HandleMessage(moho, view, msg)
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end
	if (msg == self.SELECT_SIMILAR) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local selectGroups = {}
		for i = 0, mesh:CountGroups()-1 do
			local group = mesh:Group(i)
			if group:Name() ~= "Shy" then
				for i = 0, group:CountPoints()-1 do
					local point = group:Point(i)
					if point.fSelected == true then
						table.insert(selectGroups, group)				
						break
					end
				end
			end
		end
		if #selectGroups > 0 then
			for i = 1, #selectGroups do
				group = selectGroups[i]
				for i = 0, group:CountPoints()-1 do
					local pt = group:Point(i)
					pt.fSelected = true
				end
			end
		end
	elseif (msg == self.HIDE_SELECTED_POINTS) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				pt.fHidden = true
			end
		end
		moho:UpdateUI()
	elseif (msg == self.SHOW_ALL_POINTS) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local shyGroup
		for i = 0, mesh:CountGroups()-1 do
			local group = mesh:Group(i)
			if group:Name() == "Shy" then
				shyGroup = group
			end
		end
		for i = 0, mesh:CountPoints() - 1 do
			if shyGroup == nil or not shyGroup:ContainsPointID(i) then
				local pt = mesh:Point(i)
				pt.fHidden = false
			end
		end
		moho:UpdateUI()
	elseif (msg >= self.SET_POINT_COLOR and msg <= self.SET_POINT_COLOR + 11) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local buttonColor = (msg - self.SET_POINT_COLOR)
		if buttonColor ~= 0 then
			self:ToggleSelectedPointsColor(moho, buttonColor)
		else
			for i = 1, #FO_Utilities.colorNames do
				self:RemoveSelectedPointsFromGroup(moho, FO_Utilities.colorNames[i])
			end
		end
		self:UpdateWidgets(moho)
		MOHO.Redraw() -- Needed when using shortcuts instead of clicking buttons
	elseif (msg >= self.SELECT_POINT_COLOR and msg <= self.SELECT_POINT_COLOR + 11) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local buttonColor = (msg - self.SELECT_POINT_COLOR)
		local colorName = FO_Utilities.colorNames[buttonColor]
		mesh:SelectNone()
		mesh:SelectGroup(colorName)
		-- Since "Plain" is never a group, check if point is not of any group that is named after a color instead.
		if colorName == "Plain" then
			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				local pointIsPlain = true
				for j = 0, mesh:CountGroups() - 1 do
					local group = mesh:Group(j)
					local groupName = group:Name()
					if group:ContainsPointID(i) then
						if table.contains(FO_Utilities.colorNames, groupName) then
							pointIsPlain = false
							break
						end
					end
				end
				if pointIsPlain then
					pt.fSelected = true
				end
			end
		end
		self:UpdateWidgets(moho)
		MOHO.Redraw() -- Needed when using shortcuts instead of clicking buttons
	elseif (msg == self.TOGGLE_SELECTED_POINTS_SHY) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		local groupName = "Shy"
		local doShy = self.shyButton:Value()
		if doShy then
			local startSelection = {}
			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				if (pt.fSelected) then
					table.insert(startSelection, pt)
				end
			end
			mesh:SelectGroup(groupName)
			mesh:AddGroup(groupName)
			mesh:SelectNone()
			for i = 1, #startSelection do
				local point = startSelection[i]
				point.fSelected = true
				point.fHidden = doShy
			end
			moho:UpdateUI()
		else
			self:RemoveSelectedPointsFromGroup(moho, "Shy")
			self:UpdateWidgets(moho)
		end
	elseif (msg == self.HIDE_SHOW_SHY_POINTS) then
		self:HideShowShyPoints(moho)
	elseif (msg == self.CREATE) then
		local name = self.groupName:Value()
		if (string.len(name) == 0) then
			LM.GUI.Alert(LM.GUI.ALERT_INFO, MOHO.Localize("/Scripts/Tool/SelectPoints/NameGroup=To create a group, you must first name it."))
		elseif (moho:CountSelectedPoints() < 1) then
			LM.GUI.Alert(LM.GUI.ALERT_INFO, MOHO.Localize("/Scripts/Tool/SelectPoints/CantCreateGroup=Can't create group - no points are selected."))
		else
			moho.document:PrepUndo(moho.drawingLayer)
			moho.document:SetDirty()
			mesh:AddGroup(name)
			moho:UpdateUI()
		end
	elseif (msg == self.DELETE) then
		local name = self.groupName:Value()
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		mesh:DeleteGroup(name)
		self.groupName:SetValue("")
		moho:UpdateUI()
	elseif (msg == self.LASSO) then
		self.lassoMode = self.lassoCheck:Value()
	elseif (msg == self.FLIP_H) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
	
		local center = mesh:SelectedCenter()
	
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				local f = center.x - pt.fPos.x
				pt.fPos.x = center.x + f
				pt:FlipControlHandles(moho.drawingLayerFrame)
			end
		end
	
		moho:AddPointKeyframe(moho.drawingFrame)
		moho:NewKeyframe(CHANNEL_POINT)
	elseif (msg == self.FLIP_V) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
	
		local center = mesh:SelectedCenter()
	
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				local f = center.y - pt.fPos.y
				pt.fPos.y = center.y + f
				pt:FlipControlHandles(moho.drawingLayerFrame)
			end
		end
	
		moho:AddPointKeyframe(moho.drawingFrame)
		moho:NewKeyframe(CHANNEL_POINT)
	elseif (msg == self.WELDOVERLAPS) then
		local didPrepUndo = false
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected()) then
				if (not didPrepUndo) then
					moho.document:PrepUndo(moho.drawingLayer)
					moho.document:SetDirty()
					didPrepUndo = true
				end
				local awr = LM_TransformPoints.autoWeldRadius
				LM_TransformPoints.autoWeldRadius = 3
				MOHO.WeldFullCurve(moho, moho.drawingLayer, mesh, i, moho.view)
				LM_TransformPoints.autoWeldRadius = awr
			end
		end
	elseif (msg == self.SIMPLIFYCURVE) then
		local didPrepUndo = false
		local epsilon = MOHO.SimplifyEpsilon(self.simplifyValue)
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected() or curve:IsPartiallySelected()) then
				if (not didPrepUndo) then
					moho.document:PrepUndo(moho.drawingLayer)
					moho.document:SetDirty()
					didPrepUndo = true
				end
--local beforePts = mesh:Curve(curveID):CountPoints()
				mesh:AdvancedCurveSimplification(i, moho.drawingLayerFrame, 0.0, epsilon, 0.01) -- 0.001, 0.0001, 0.01
--print(beforePts .. "/" .. mesh:Curve(curveID):CountPoints())
--				MOHO.ReduceCurve(moho, mesh, i)
			end
		end
		moho:UpdateUI()
	elseif (msg == self.CHANGE_SIMPLIFY) then
		self.simplifyValue = LM.Clamp(self.simplifyText:Value(), 0, 100)
		self:UpdateWidgets(moho)
	elseif (msg == self.SPLIT) then
		local selectedEdge = false
		for i = 0, mesh:CountCurves() - 1 do
			local curve = mesh:Curve(i)
			if (curve:IsSelected() or curve:IsPartiallySelected()) then
				selectedEdge = true
			end
		end
		if (selectedEdge) then
			moho.document:PrepUndo(moho.drawingLayer)
			moho.document:SetDirty()

			MOHO:SplitSelectedSegments(mesh, 1, moho.drawingLayerFrame)
		end
		moho:UpdateUI()
	elseif (msg == self.SLINK_X) then
		-- * LK_SelectPoints:Slink_Points(moho, horizontal, vertical)
		self:Slink_Points(moho, true, false)
	elseif (msg == self.SLINK_Y) then
		self:Slink_Points(moho, false, true)
	elseif (msg == self.SLINK) then
		self:Slink_Points(moho, true, true)
	elseif (msg == self.CURVE_PEAK) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				pt:SetCurvature(MOHO.PEAKED, moho.drawingLayerFrame)
				pt:ResetControlHandles(moho.drawingLayerFrame)
			end
		end
		moho:NewKeyframe(CHANNEL_CURVE)
		moho:UpdateUI()
	elseif (msg == self.CURVE_SMOOTH) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				pt:SetCurvature(MOHO.SMOOTH, moho.drawingLayerFrame)
				pt:ResetControlHandles(moho.drawingLayerFrame)
			end
		end
		moho:NewKeyframe(CHANNEL_CURVE)
		moho:UpdateUI()
	elseif (msg == self.SELECT_CONNECTED) then
		mesh:SelectConnected()
		self:UpdateWidgets(moho)
	elseif (msg == self.WELD_ENDS) then
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		self:WeldClosestEnds(moho)
		moho:Click()
		-- * Select entire curve:
		mesh:SelectConnected()
		-- * Update widgets:
		self:UpdateWidgets(moho)
	elseif (msg == self.FILL_POINTS) then
		-- * Prepare undo:
		moho.document:PrepUndo(moho.drawingLayer)
		moho.document:SetDirty()
		-- * Save point selection:
		local savedPointSelection = {}
		for i = 0, mesh:CountPoints() - 1 do
			local pt = mesh:Point(i)
			if (pt.fSelected) then
				table.insert(savedPointSelection, i)
			end
		end
		-- * Flood select points:
		self:HandleMessage(moho, view, self.SELECT_CONNECTED)
		-- * Weld multiple curve endpoints if nessecary:
		while self:AtLeast2EndpointsOn1Curve(moho) do
			self:WeldClosestEnds(moho)
			self:UpdateWidgets(moho)
		end
		local shapeID = -1
		local shape = nil
		shapeID = moho:CreateShape(true) -- filled shape
		if (shapeID >= 0) then
			shape = mesh:Shape(shapeID)
			shape.fHasOutline = false
		end
		if (shapeID >= 0) then
			--mesh:SelectNone()
			if (shape.fHasFill) then
				moho:NewKeyframe(CHANNEL_FILL)
			end
			--mesh:LowerShape(shapeID, true) -- toBottom should actually be false, and it should figure out how often it should be lowered...

		
			-- * Revert to old point selection:
			local shapesOnPoints = {}
			for i = 0, mesh:CountPoints() - 1 do
				local pt = mesh:Point(i)
				if table.contains(savedPointSelection, i) then
					pt.fSelected = true
					for j = 0, mesh:CountShapes() - 1 do
						local shape = mesh:Shape(j)
						if shape:ContainsPoint(i) then
							if not table.contains(shapesOnPoints, j) then
								table.insert(shapesOnPoints, j)
							end
						end
					end
				else
					pt.fSelected = false
				end
			end
			--
			-- * Place fill shape behind lowest line shape:
			table.sort(shapesOnPoints, function(a,b) return a < b end)
			local lowestShapeID = shapesOnPoints[1]
			mesh:PlaceShapeBehind(shapeID, lowestShapeID)
		end
		moho:UpdateSelectedChannels()
		moho:UpdateUI()
		-- * Update view:
		view:DrawMe()
	elseif (msg >= self.SELECTITEM) then
		mesh:SelectNone()
		local i = msg - self.SELECTITEM
		local name = mesh:Group(i):Name()
		mesh:SelectGroup(name)
		self.groupName:SetValue(name)
		moho:UpdateUI()
	end
	FO_Utilities:PointsColorKeys(moho)
end
--
function LK_SelectPoints:WeldClosestEnds(moho, peakEnds)
	peakEnds = peakEnds or true
	local mesh = moho:DrawingMesh()
	-- * Find end points:
	local endpointIDs = {}
	local endpoints = {}
	for i = 0, mesh:CountPoints() - 1 do
		local pt = mesh:Point(i)
		if (pt.fSelected) then
			if pt:IsEndpoint() then
				table.insert(endpointIDs, i)
				table.insert(endpoints, pt)
			end
		end
	end
	--
	local shortestDistance
	local movingPointID
	local solidPointID
	local movingPoint
	local solidPoint
	for i = 1, #endpoints do
		local firstEndPoint = endpoints[i]
		for j = 1, #endpoints do
			local otherEndPoint = endpoints[j]
			if firstEndPoint ~= otherEndPoint then
				if firstEndPoint:Curve(0) ~= otherEndPoint:Curve(0) or #endpoints == 2 then
					local distance = FO_Utilities:Distance(moho, firstEndPoint.fPos, otherEndPoint.fPos)
					if shortestDistance == nil or distance < shortestDistance then
						shortestDistance = distance
						movingPoint = firstEndPoint
						solidPoint = otherEndPoint
						movingPointID = mesh:PointID(firstEndPoint)
						solidPointID = mesh:PointID(otherEndPoint)
					end
				end
			end
		end
	end
	local pos = mesh:Point(movingPointID).fPos
	-- * Add point:
	mesh:AddPoint(pos, mesh:PointID(movingPoint), 0)
	local newPoint = mesh:Point(mesh:CountPoints()-1)
	local newPointID = mesh:PointID(newPoint)
	-- * Weld point:
	mesh:WeldPoints(newPointID, solidPointID, 0)
	if peakEnds then
		-- *** Peak corners:
		-- * Peak new point:
		movingPoint:SetCurvature(MOHO.PEAKED, 0)
		movingPoint:ResetControlHandles(0)
		-- * Peak existing end point:
		solidPoint:SetCurvature(MOHO.PEAKED, 0)
		solidPoint:ResetControlHandles(0)
	end
end

-- *
function LK_SelectPoints:Slink_Points(moho, horizontal, vertical)
	-- * Get mesh:
	local mesh = moho:DrawingMesh()
	if (mesh == nil) then
		return
	end
	-- * Prep undo:
	moho.document:PrepUndo(moho.layer)
	moho.document:SetDirty()
	-- * Set horizontal/vertical options:
	local x = 1
	local y = 1
	if horizontal then
		x = 0
	end
	if vertical then
		y = 0
	end
	-- * Slink points:
	mesh:PrepMovePoints()
	mesh:ScalePoints(x, y, mesh:SelectedCenter())
	moho:AddPointKeyframe(moho.drawingFrame, nil, MOHO.MohoGlobals.EditMultipleKeys)
	moho:NewKeyframe(CHANNEL_POINT)
end

Icon
Select Points
Listed

Author: Lukas View Script
Script type: Tool

Uploaded: Jun 02 2022, 08:05

Last modified: Sep 20 2024, 01:59

Script Version: 6.4

Select Points mod with Color-points, Shy-points and more
Image
Modded version of the Select Points tool with the ability to color points and set shy points just as you would set shy bones. Color points are basically point groups, but much faster to work with.

Ctrl+Alt+Shift+Click on a point to select all points of that color.

Ctrl+Click a point to select that entire curve (no longer need to click on an edge to select curve) (Include shift to add to and alt to subtract from selection)

Press Slash to create a fill based on selected points, it will close the curve. Selected points do not need to be on the same curve and you do not need to select all points.

Changelog:
Version 6.2 Supports Moho 14

Note:
If you want to replace the original tool, simply edit the "YourCustomContentFolder/Moho Pro/Scripts/Tool/_tool_list.txt" file and make sure you put "LK_SelectPoints" BELOW "lm_select_points", it will remove the original tool from your toolpanel if LK_SelectPoints is installed.
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: 1664