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

ScriptName = "AM_Create_Limb_V2"

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

AM_Create_Limb_V2 = {}

function AM_Create_Limb_V2:Name()
	return 'Create Limb 2'
end

function AM_Create_Limb_V2:Version()
	return '1.0'
end

function AM_Create_Limb_V2:UILabel()
	return 'Create Limb 2'
end

function AM_Create_Limb_V2:Creator()
	return 'Aleksei Maletin'
end

function AM_Create_Limb_V2:Description()
	return 'Hold <Alt> and drag side to side to adjust radius of both joints. Hold <Shift> and drag side to side to adjust radius of end joint'
end


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

function AM_Create_Limb_V2:IsRelevant(moho)
	if moho:Skeleton() then
		return true
	else
		return false
	end
end

function AM_Create_Limb_V2:IsEnabled(moho)
	if moho:Skeleton() then
		return true
	else
		return false
	end
end

-- **************************************************
-- Recurring Values
-- **************************************************

AM_Create_Limb_V2.baseBodypart = true
AM_Create_Limb_V2.endOfForearmShin = false
AM_Create_Limb_V2.startRadius = 0
AM_Create_Limb_V2.endRadius = 0
AM_Create_Limb_V2.bodyPartName = 'Forearm L'
AM_Create_Limb_V2.stroke = false
AM_Create_Limb_V2.fill = false
AM_Create_Limb_V2.startJoint = true
AM_Create_Limb_V2.startJoint2 = true
AM_Create_Limb_V2.startJointTest = true
AM_Create_Limb_V2.endJoint = true
AM_Create_Limb_V2.endJoint2 = true
AM_Create_Limb_V2.makeTangentForm = true
AM_Create_Limb_V2.makeTangentForm2 = true
AM_Create_Limb_V2.createDeformer = false
AM_Create_Limb_V2.magnitude = 0
AM_Create_Limb_V2.selRect = LM.Rect:new_local()
AM_Create_Limb_V2.currentShapeSettings = 1 -- 1 for Base bodypart, 2 for end of forearm/shin
AM_Create_Limb_V2.sleeveLength = 1.00
AM_Create_Limb_V2.startRadius2 = 0
AM_Create_Limb_V2.endRadius2 = 0
AM_Create_Limb_V2.magnitude2 = 0
AM_Create_Limb_V2.sleeveDefault = 1
AM_Create_Limb_V2.sleeve = 1

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


function AM_Create_Limb_V2:LoadPrefs(prefs)
	self.baseBodypart = prefs:GetBool("AM_Create_Limb_V2.baseBodypart", true)
	self.endOfForearmShin = prefs:GetBool("AM_Create_Limb_V2.endOfForearmShin", false)
	self.startRadius = prefs:GetFloat("AM_Create_Limb_V2.startRadius", 0)
	self.endRadius = prefs:GetFloat("AM_Create_Limb_V2.endRadius", 0)
	self.bodyPartName = prefs:GetString("AM_Create_Limb_V2.bodyPartName", 'Forearm L')
	self.stroke = prefs:GetBool("AM_Create_Limb_V2.stroke", false)
	self.fill = prefs:GetBool("AM_Create_Limb_V2.fill", false)
	self.startJoint = prefs:GetBool("AM_Create_Limb_V2.startJoint", true)
	self.endJoint = prefs:GetBool("AM_Create_Limb_V2.endJoint", true)
	self.startJoint2 = prefs:GetBool("AM_Create_Limb_V2.startJoint2", true)
	self.endJoint2 = prefs:GetBool("AM_Create_Limb_V2.endJoint2", true)
	self.makeTangentForm = prefs:GetBool("AM_Create_Limb_V2.makeTangentForm", true)
	self.makeTangentForm2 = prefs:GetBool("AM_Create_Limb_V2.makeTangentForm2", true)
	self.createDeformer = prefs:GetBool("AM_Create_Limb_V2.createDeformer", false)
	self.currentShapeSettings = prefs:GetFloat("AM_Create_Limb_V2.currentShapeSettings", 1)
	self.sleeveLength = prefs:GetFloat("AM_Create_Limb_V2.sleeveLength", 1.00)
	self.startRadius2 = prefs:GetFloat("AM_Create_Limb_V2.startRadius2", 0)
	self.endRadius2 = prefs:GetFloat("AM_Create_Limb_V2.endRadius2", 0)
	self.magnitude = prefs:GetFloat("AM_Create_Limb_V2.magnitude", 0)
    self.magnitude2 = prefs:GetFloat("AM_Create_Limb_V2.magnitude2", 0)

end

function AM_Create_Limb_V2:SavePrefs(prefs)
	prefs:SetBool("AM_Create_Limb_V2.baseBodypart", self.baseBodypart)
	prefs:SetBool("AM_Create_Limb_V2.endOfForearmShin", self.endOfForearmShin)
	prefs:SetFloat("AM_Create_Limb_V2.startRadius", self.startRadius)
	prefs:SetFloat("AM_Create_Limb_V2.endRadius", self.endRadius)
	prefs:SetString("AM_Create_Limb_V2.bodyPartName", self.bodyPartName)
	prefs:SetBool("AM_Create_Limb_V2.stroke", self.stroke)
	prefs:SetBool("AM_Create_Limb_V2.fill", self.fill)
	prefs:SetBool("AM_Create_Limb_V2.startJoint", self.startJoint)
	prefs:SetBool("AM_Create_Limb_V2.endJoint", self.endJoint)
	prefs:SetBool("AM_Create_Limb_V2.startJoint2", self.startJoint2)
	prefs:SetBool("AM_Create_Limb_V2.endJoint2", self.endJoint2)
	prefs:SetBool("AM_Create_Limb_V2.makeTangentForm", self.makeTangentForm)
	prefs:SetBool("AM_Create_Limb_V2.makeTangentForm2", self.makeTangentForm2)
	prefs:SetBool("AM_Create_Limb_V2.createDeformer", self.createDeformer)
	prefs:SetFloat("AM_Create_Limb_V2.currentShapeSettings", self.currentShapeSettings)
	prefs:SetFloat("AM_Create_Limb_V2.sleeveLength", self.sleeveLength)
	prefs:SetFloat("AM_Create_Limb_V2.startRadius2", self.startRadius2)
	prefs:SetFloat("AM_Create_Limb_V2.endRadius2", self.endRadius2)
	prefs:SetFloat("AM_Create_Limb_V2.magnitude", self.magnitude)
    prefs:SetFloat("AM_Create_Limb_V2.magnitude2", self.magnitude2)
end

function AM_Create_Limb_V2:ResetPrefs()
	self.baseBodypart = true
	self.endOfForearmShin = false
	self.startRadius = 0
	self.endRadius = 0
	self.bodyPartName = 'Forearm L'
	self.stroke = false
	self.fill = false
	self.startJoint = true
	self.endJoint = true
	self.startJoint2 = true
	self.endJoint2 = true
	self.makeTangentForm = true
	self.makeTangentForm2 = true
	self.createDeformer = false
	self.currentShapeSettings = 1
	self.sleeveLength = 1.00
	self.startRadius2 = 0
	self.endRadius2 = 0
	self.magnitude = 0
    self.magnitude2 = 0
end

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

function AM_Create_Limb_V2:OnMouseDown(moho, mouseEvent)

	if not (mouseEvent.altKey) or not (mouseEvent.shiftKey) then
		self.isMouseDragging = true
	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()
	self.savedVal = 0
	self:Select(moho, mouseEvent.pt, mouseEvent.vec, mouseEvent.view, mouseEvent.shiftKey, mouseEvent.ctrlKey, mouseEvent.altKey)

end

function AM_Create_Limb_V2:OnMouseMoved(moho, mouseEvent)
	local skel = moho:Skeleton()

	if (mouseEvent.altKey) then
		
		if (skel == nil) then
			return
		end

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

		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)

			if (bone.fSelected) then

				if self.baseBodypart then
					self.startRadius = self.startRadius - self.savedVal + newVal
					self.endRadius = self.startRadius + self.magnitude

					if (self.startRadius < 0) then
						self.startRadius = 0
					end

					if (self.magnitude < 0 - self.startRadius) then
						self.magnitude = 0 - self.startRadius
					end

				elseif self.endOfForearmShin then

					self.startRadius2 = self.startRadius2 - self.savedVal + newVal
					self.endRadius2 = self.startRadius2 + self.magnitude2

					if (self.startRadius2 < 0) then
						self.startRadius2 = 0
					end

					if (self.magnitude2 < 0 - self.startRadius2) then
						self.magnitude2 = 0 - self.startRadius2
					end

				end

			end

		end

		self.savedVal = newVal

	elseif (mouseEvent.shiftKey) then

		if (skel == nil) then
			return
		end

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

		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)

			if (bone.fSelected) then

				if self.baseBodypart then
					self.magnitude = self.magnitude - self.savedVal + newVal
					self.endRadius = self.startRadius + self.magnitude

					if (self.magnitude < 0 - self.startRadius) then
						self.magnitude = 0 - self.startRadius
					end

				elseif self.endOfForearmShin then
					self.magnitude2 = self.magnitude2 - self.savedVal + newVal
				
					self.endRadius2 = self.startRadius2 + self.magnitude2

					if (self.magnitude2 < 0 - self.startRadius2) then
						self.magnitude2 = 0 - self.startRadius2
					end

				end

			end

		end

		self.savedVal = newVal

	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

function AM_Create_Limb_V2:OnMouseUp(moho, mouseEvent)

	local skel = moho:Skeleton()
	local mouseDist = math.abs(mouseEvent.pt.x - mouseEvent.startPt.x) + math.abs(mouseEvent.pt.y - mouseEvent.startPt.y)
	self.isMouseDragging = false
	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)

	if skel then
		
		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)
			local boneMatrix = bone.fMovedMatrix

			for j = 0, 10 do
				v:Set(bone.fLength * j / 10.0, 0)
				boneMatrix:Transform(v)
				m:Transform(v)
				mouseEvent.view:Graphics():WorldToScreen(v, screenPt)

				if (self.selRect:Contains(screenPt)) then

						if mouseEvent.ctrlKey then
							bone.fSelected = not bone.fSelected
						else
							bone.fSelected = true
						end
						
					break

				end

			end

		end

	end

	moho:UpdateSelectedChannels()
end

function AM_Create_Limb_V2:DrawMe(moho, view)
	local skel = moho:Skeleton()

	if skel then
		local g = view:Graphics()
		local matrix = LM.Matrix:new_local()
		local bonePt = LM.Vector2:new_local()
		local bonePt2 = LM.Vector2:new_local()
		local bonePt3 = LM.Vector2:new_local()

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

		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)

			if bone.fSelected then
				
				local parentBone = skel:Bone(bone.fAngleControlParent)

				bonePt:Set(bone.fLength*self.sleeve, 0)
				bone.fMovedMatrix:Transform(bonePt)

				bonePt2:Set(0, 0)
				bone.fMovedMatrix:Transform(bonePt2)
				 
				bonePt3:Set(bone.fLength, 0)
				bone.fMovedMatrix:Transform(bonePt3)

				if self.startJoint then
					g:SetColor(50, 255, 50, 50)
					g:FillCircle(bonePt2, self.startRadius)

					if self.endOfForearmShin then
						g:SetColor(255, 255, 50, 50)
						g:FillCircle(bonePt2, self.startRadius2)
					end

				end

				if self.endJoint and self.makeTangentForm then

					if not self.endOfForearmShin then
						g:SetColor(50, 255, 50, 50)
						g:FillCircle(bonePt, self.endRadius)
					end

				elseif self.endJoint then

					if not self.endOfForearmShin then
						g:SetColor(50, 255, 50, 50)
						g:FillCircle(bonePt, self.startRadius)
					end

				end
				
				local boneLength = bone.fLength
				local centerA = LM.Vector2:new_local()
				centerA:Set(bone)
				bone.fMovedMatrix:Transform(centerA)
				local centerB = LM.Vector2:new_local()
				centerB:Set(bone.fLength)
				bone.fMovedMatrix:Transform(centerB)

				-- draw wrist rectangle

				if self.endOfForearmShin then

					if self.makeTangentForm2 then

						if self.startRadius2 ~= 0 or self.endRadius2 ~= 0 then
							local pt1Coord, pt2Coord, pt3Coord, pt4Coord = self:BodyPartPointsCoords(centerA, centerB, self.startRadius2, self.endRadius2)
							g:SetColor(255, 255, 50, 50)
							g:BeginShape()
							g:AddLine(pt1Coord, pt2Coord)
							g:AddLine(pt2Coord, pt3Coord)
							g:AddLine(pt3Coord, pt4Coord)
							g:AddLine(pt4Coord, pt1Coord)
							g:EndShape()

							if self.endJoint2 then
								g:SetColor(255, 255, 50, 50)
								g:FillCircle(bonePt3, self.endRadius2)
							end

						end

					else

						if self.startRadius2 ~= 0 then
							local pt1Coord, pt2Coord, pt3Coord, pt4Coord = self:BodyPartPointsCoords(centerA, centerB, self.startRadius2, self.startRadius2)
							g:SetColor(255, 255, 50, 50)
							g:BeginShape()
							g:AddLine(pt1Coord, pt2Coord)
							g:AddLine(pt2Coord, pt3Coord)
							g:AddLine(pt3Coord, pt4Coord)
							g:AddLine(pt4Coord, pt1Coord)
							g:EndShape()

							if self.endJoint2 then
								g:SetColor(255, 255, 50, 50)
								g:FillCircle(bonePt3, self.startRadius2)
							end

						end
					end
					
				end



				if self.makeTangentForm then
					local pt1Coord, pt2Coord, pt3Coord, pt4Coord = self:BodyPartPointsCoords(centerA, bonePt, self.startRadius, self.endRadius)
					g:SetColor(50, 255, 50, 50)
					g:BeginShape()
					g:AddLine(pt1Coord, pt2Coord)
					g:AddLine(pt2Coord, pt3Coord)
					g:AddLine(pt3Coord, pt4Coord)
					g:AddLine(pt4Coord, pt1Coord)
					g:EndShape()

				else
					local pt1Coord, pt2Coord, pt3Coord, pt4Coord = self:BodyPartPointsCoords(centerA, bonePt, self.startRadius, self.startRadius)
					g:SetColor(50, 255, 50, 50)
					g:BeginShape()
					g:AddLine(pt1Coord, pt2Coord)
					g:AddLine(pt2Coord, pt3Coord)
					g:AddLine(pt3Coord, pt4Coord)
					g:AddLine(pt4Coord, pt1Coord)
					g:EndShape()
				
				end

			end

		end

		if self.isMouseDragging then
			local g = view:Graphics()
			g:SelectionRect(self.selRect)
		end

		g:Pop()
	end

end

-- **************************************************
-- guts of script
-- **************************************************


function AM_Create_Limb_V2:Select(moho, mousePt, mouseVec, mouseView, shiftSelect, ctrlSelect, altSelect)
	self.selBoneID = -1

	local parentSkeleton = false
	local skel = moho:Skeleton()

	if (skel == nil) then

		if (self:ShouldUseParentSkeleton(moho)) then
			skel = moho:ParentSkeleton()
			parentSkeleton = true
		end

		if (skel == nil) then
			return
		end

	end

	local id = -1

	if (parentSkeleton) then
		id = mouseView:PickBone(mousePt, mouseVec, moho.layer:ControllingBoneLayer(), true)
	else
		id = mouseView:PickBone(mousePt, mouseVec, moho.layer, true)
	end

	self.selBoneID = id

	if (shiftSelect) then

		if (id >= 0) then
			skel:Bone(id).fSelected = true
		end

	elseif (ctrlSelect) then

		if (id >= 0) then
			skel:Bone(id).fSelected = not skel:Bone(id).fSelected
		end

	elseif (altSelect) then

	else

		for i = 0, skel:CountBones() - 1 do
			skel:Bone(i).fSelected = (i == id)
		end

	end

	moho:UpdateBonePointSelection()
	mouseView:DrawMe()
	moho:UpdateSelectedChannels()

end


function AM_Create_Limb_V2:BodyPartPointsCoords(centerA, centerB, radiusA, radiusB)
	
	local Yplus = math.atan2(centerB.x - centerA.x, centerB.y - centerA.y) + math.acos((radiusA - radiusB)/math.sqrt((centerB.x - centerA.x)^2+(centerB.y - centerA.y)^2))
	local Yminus = math.atan2(centerB.x - centerA.x, centerB.y - centerA.y) - math.acos((radiusA - radiusB)/math.sqrt((centerB.x - centerA.x)^2+(centerB.y - centerA.y)^2))
	local pt1Coord = LM.Vector2:new_local()
	local pt2Coord = LM.Vector2:new_local()
	local pt3Coord = LM.Vector2:new_local()
	local pt4Coord = LM.Vector2:new_local()

	local YplusCoords = LM.Vector2:new_local()
	local YminusCoords = LM.Vector2:new_local()
	YplusCoords.y = math.cos(Yminus)
	YplusCoords.x = math.sin(Yminus)
	YminusCoords.y = math.cos(Yplus)
	YminusCoords.x = math.sin(Yplus)

	local pt1CoordPlus =  LM.Vector2:new_local()
	pt1CoordPlus.x = radiusA * YminusCoords.x
	pt1CoordPlus.y = radiusA * YminusCoords.y

	pt1Coord:Set(centerA + pt1CoordPlus)

	local pt2CoordPlus = LM.Vector2:new_local()

	pt2CoordPlus.x = radiusA * YplusCoords.x
	pt2CoordPlus.y = radiusA * YplusCoords.y

	pt2Coord:Set(centerA + pt2CoordPlus)

	local pt4CoordPlus = LM.Vector2:new_local()
	pt4CoordPlus.x = radiusB * YminusCoords.x
	pt4CoordPlus.y = radiusB * YminusCoords.y

	pt4Coord:Set(centerB + pt4CoordPlus)

	local pt3CoordPlus = LM.Vector2:new_local()
	pt3CoordPlus.x = radiusB * YplusCoords.x
	pt3CoordPlus.y = radiusB * YplusCoords.y

	pt3Coord:Set(centerB + pt3CoordPlus)

	return pt1Coord, pt2Coord, pt3Coord, pt4Coord

end

function AM_Create_Limb_V2:GetGlobalBonePos(moho, bone)
	local skel = moho:Skeleton()
	local selBonePos = LM.Vector2:new_local()
	selBonePos:Set(bone.fAnimPos:GetValue(0))

	if bone.fParent >-1 then
	  local selBoneParentMatrix = LM.Matrix:new_local()
	  selBoneParentMatrix:Set(skel:Bone(bone.fParent).fRestMatrix)
	  selBoneParentMatrix:Transform(selBonePos)
	end

	return selBonePos
end

function AM_Create_Limb_V2:GetGlobalBoneEndPos(moho, bone)

end

function AM_Create_Limb_V2:ShowConstructionCurvesTest(moho, layer)

	if self.stroke then
		layer:ShowConstructionCurves(false)
	elseif self.fill then
		layer:ShowConstructionCurves(false)
	else
		layer:ShowConstructionCurves(true)
	end

end

function AM_Create_Limb_V2:DrawCircle(destMesh, vec, radius, parentBone)
	local n = destMesh:CountPoints()		
	
	vec.x = vec.x - radius
	destMesh:AddLonePoint(vec, 0)
	
	vec.x = vec.x + radius
	vec.y = vec.y - radius
	destMesh:AppendPoint(vec, 0)
	
	vec.x = vec.x + radius
	vec.y = vec.y + radius
	destMesh:AppendPoint(vec, 0)
	
	vec.x = vec.x - radius
	vec.y = vec.y + radius
	destMesh:AppendPoint(vec, 0)
	
	vec.x = vec.x - radius
	vec.y = vec.y - radius
	destMesh:AppendPoint(vec, 0)

	destMesh:WeldPoints(n + 4, n, 0)

	-- set all their curvatures appropriately (0.391379 is the magic number for a circle)
	destMesh:SelectNone()
	destMesh:Point(n):SetCurvature(0.391379, 0)
	destMesh:Point(n).fParent = parentBone
	destMesh:Point(n + 1):SetCurvature(0.391379, 0)
	destMesh:Point(n + 1).fParent = parentBone
	destMesh:Point(n + 2):SetCurvature(0.391379, 0)
	destMesh:Point(n + 2).fParent = parentBone
	destMesh:Point(n + 3):SetCurvature(0.391379, 0)
	destMesh:Point(n + 3).fParent = parentBone

	destMesh:Point(n).fSelected = true

	destMesh:SelectConnected(n + 3)

end

function AM_Create_Limb_V2:GetGlobalAngle(moho, v1, v2)
	v2 = v2 - v1
	local newAngle = math.atan2(v2.y, v2.x)
	return newAngle
end

function AM_Create_Limb_V2:BindJointToBone(jointsMesh, pt1, pt2, pt3, pt4, parentBoneID)
	jointsMesh:Point(pt1).fParent = parentBoneID
	jointsMesh:Point(pt2).fParent = parentBoneID
	jointsMesh:Point(pt3).fParent = parentBoneID
	jointsMesh:Point(pt4).fParent = parentBoneID
end

function AM_Create_Limb_V2:DrawBodyPart(destMesh, centerA, centerB, radiusA, radiusB, parentStart, parentEnd)

	centerA.x = centerA.x + radiusA
	centerB.x = centerB.x + radiusB

	local n = destMesh:CountPoints()

	local Yplus = math.atan2(centerB.x - centerA.x, centerB.y - centerA.y) + math.acos((radiusA - radiusB)/math.sqrt((centerB.x - centerA.x)^2+(centerB.y - centerA.y)^2))
	local Yminus = math.atan2(centerB.x - centerA.x, centerB.y - centerA.y) - math.acos((radiusA - radiusB)/math.sqrt((centerB.x - centerA.x)^2+(centerB.y - centerA.y)^2))
	local pt1Coord = LM.Vector2:new_local()
	local pt2Coord = LM.Vector2:new_local()
	local pt3Coord = LM.Vector2:new_local()
	local pt4Coord = LM.Vector2:new_local()

	local YplusCoords = LM.Vector2:new_local()
	local YminusCoords = LM.Vector2:new_local()
	YplusCoords.y = math.cos(Yminus)
	YplusCoords.x = math.sin(Yminus)
	YminusCoords.y = math.cos(Yplus)
	YminusCoords.x = math.sin(Yplus)

	local pt1CoordPlus =  LM.Vector2:new_local()
	pt1CoordPlus.x = radiusA * YminusCoords.x
	pt1CoordPlus.y = radiusA * YminusCoords.y

	pt1Coord = centerA + pt1CoordPlus

	local pt2CoordPlus = LM.Vector2:new_local()

	pt2CoordPlus.x = radiusA * YplusCoords.x
	pt2CoordPlus.y = radiusA * YplusCoords.y

	pt2Coord = centerA + pt2CoordPlus

	local pt4CoordPlus = LM.Vector2:new_local()
	pt4CoordPlus.x = radiusB * YminusCoords.x
	pt4CoordPlus.y = radiusB * YminusCoords.y

	pt4Coord = centerB + pt4CoordPlus

	local pt3CoordPlus = LM.Vector2:new_local()
	pt3CoordPlus.x = radiusB * YplusCoords.x
	pt3CoordPlus.y = radiusB * YplusCoords.y

	pt3Coord = centerB + pt3CoordPlus

	
	destMesh:AddLonePoint(pt1Coord, 0)
	
	destMesh:AppendPoint(pt2Coord, 0)
	
	destMesh:AppendPoint(pt3Coord, 0)
	
	destMesh:AppendPoint(pt4Coord, 0)

	destMesh:AppendPoint(pt1Coord, 0)
	
	destMesh:WeldPoints(n + 4, n, 0)
	destMesh:SelectNone()

	destMesh:Point(n):SetCurvature(0, 0)
	destMesh:Point(n).fParent = parentStart
	destMesh:Point(n + 1):SetCurvature(0, 0)
	destMesh:Point(n + 1).fParent = parentStart
	destMesh:Point(n + 2):SetCurvature(0, 0)
	destMesh:Point(n + 2).fParent = parentEnd
	destMesh:Point(n + 3):SetCurvature(0, 0)
	destMesh:Point(n + 3).fParent = parentEnd

	destMesh:Point(n).fSelected = true

	destMesh:SelectConnected()


end

function AM_Create_Limb_V2:CreateBodypartShape(moho, bodypartMesh)

	if self.stroke or self.fill then
		local jointsShapeID = moho:CreateShape(true)
		local jointsShape = bodypartMesh:Shape(jointsShapeID)
		jointsShape.fHasFill = self.fill
		jointsShape.fHasOutline = self.stroke
	end

end

function AM_Create_Limb_V2:ConstructionBoneSettings(moho, bone, iBone, angle, length, name)
	bone.fParent = iBone
	bone.fAnimParent:SetValue(0, iBone)
	bone.fAnimAngle:SetValue(0, math.rad(angle))
	bone.fIgnoredByIK = true
	bone.fScalingMode = 1
	bone:SetName(self.bodyPartName .. name)
	bone.fShy = true
	bone.fLength = length
	bone.fStrength = 0
end
-- **************************************************
-- Tool Panel Layout
-- **************************************************

AM_Create_Limb_V2.CREATE_LIMB = MOHO.MSG_BASE
AM_Create_Limb_V2.START_RADIUS = MOHO.MSG_BASE + 1
AM_Create_Limb_V2.END_RADIUS = MOHO.MSG_BASE + 2
AM_Create_Limb_V2.BODYPART_NAME = MOHO.MSG_BASE + 3
AM_Create_Limb_V2.STROKE = MOHO.MSG_BASE + 4
AM_Create_Limb_V2.FILL = MOHO.MSG_BASE + 5
AM_Create_Limb_V2.READ_RADIUS = MOHO.MSG_BASE + 6
AM_Create_Limb_V2.START_JOINT = MOHO.MSG_BASE + 7
AM_Create_Limb_V2.END_JOINT = MOHO.MSG_BASE + 8
AM_Create_Limb_V2.MAKE_TANGENT_FORM = MOHO.MSG_BASE + 9
AM_Create_Limb_V2.GROUP_AND_SEPARATE = MOHO.MSG_BASE + 10
AM_Create_Limb_V2.CREATE_DEFORMER = MOHO.MSG_BASE + 11
AM_Create_Limb_V2.SLEEVE_LENGTH = MOHO.MSG_BASE + 12

AM_Create_Limb_V2.BASE_BODYPART = MOHO.MSG_BASE + 100
AM_Create_Limb_V2.END_OF_FOREARMSHIN = MOHO.MSG_BASE + 101
AM_Create_Limb_V2.settingsForBaseBodypart = AM_Create_Limb_V2.BASE_BODYPART
AM_Create_Limb_V2.settingsForForearmShin = AM_Create_Limb_V2.END_OF_FOREARMSHIN

function AM_Create_Limb_V2:DoLayout(moho, layout)
	--CREATE LIMB BUTTON
	self.createLimbButton = LM.GUI.Button('Create Limb', self.CREATE_LIMB)
	layout:AddChild(self.createLimbButton, LM.GUI.ALIGN_LEFT, 0)

	-- Settings selection menu
	self.settingsForMenu = LM.GUI.Menu("Settings for:      ")
	self.settingsForMenu_popup = LM.GUI.PopupMenu(110, true)
	self.settingsForMenu_popup:SetMenu(self.settingsForMenu)
	self.settingsForMenu:AddItem("Settings for base bodypart", 0, self.BASE_BODYPART)
	self.settingsForMenu:AddItem("Settings for end of forearm/shin", 0, self.END_OF_FOREARMSHIN)
	layout:AddChild(self.settingsForMenu_popup)

	-- Start radius
	self.startRadiusInput = LM.GUI.TextControl(0, '0.0000', self.START_RADIUS, LM.GUI.FIELD_UFLOAT, 'Start radius')
	self.startRadiusInput:SetWheelInc(0.001)
	layout:AddChild(self.startRadiusInput, LM.GUI.ALIGN_LEFT, 0)

	-- End radius
	self.endRadiusInput = LM.GUI.TextControl(0, '0.0000', self.END_RADIUS, LM.GUI.FIELD_UFLOAT, 'End radius')
	self.endRadiusInput:SetWheelInc(0.001)
	layout:AddChild(self.endRadiusInput, LM.GUI.ALIGN_LEFT, 0)

	-- Bodypart name
	self.bodyPartNameInput = LM.GUI.TextControl(0, 'Forearm L', self.BODYPART_NAME, LM.GUI.FIELD_TEXT, 'Bodypart name:')
	layout:AddChild(self.bodyPartNameInput, LM.GUI.ALIGN_LEFT, 0)

	-- Stroke
	self.strokeCheckbox = LM.GUI.CheckBox('Stroke', self.STROKE)
	layout:AddChild(self.strokeCheckbox, LM.GUI.ALIGN_LEFT, 0)

	-- Fill
	self.fillCheckbox = LM.GUI.CheckBox('Fill', self.FILL)
	layout:AddChild(self.fillCheckbox, LM.GUI.ALIGN_LEFT, 0)

	-- Read radius from selected bone
	self.readRadius = LM.GUI.ImageButton('ScriptResources/am_create_limb/readradius', 'Radius = selected bone\'s length', false, self.READ_RADIUS, false)
	layout:AddChild(self.readRadius, LM.GUI.ALIGN_LEFT, 0)

	-- Start Joint
	self.startJointCheckbox = LM.GUI.ImageButton('ScriptResources/am_create_limb/startjoint', 'Start joint', true, self.START_JOINT, false)
	layout:AddChild(self.startJointCheckbox, LM.GUI.ALIGN_LEFT, 0)

	-- End Joint
	self.endJointCheckbox = LM.GUI.ImageButton('ScriptResources/am_create_limb/endjoint', 'End joint', true, self.END_JOINT, false)
	layout:AddChild(self.endJointCheckbox, LM.GUI.ALIGN_LEFT, 0)

	-- Make tangent form
	self.makeTangentFormCheckbox = LM.GUI.ImageButton('ScriptResources/am_create_limb/maketangentform', 'Make tangent form', true, self.MAKE_TANGENT_FORM, false)
	layout:AddChild(self.makeTangentFormCheckbox, LM.GUI.ALIGN_LEFT, 0)

	-- Sleeve Length settings
	self.sleeveLengthInput = LM.GUI.TextControl(0, '1.00    x', self.SLEEVE_LENGTH, LM.GUI.FIELD_UFLOAT, 'Sleeve Length:')
	self.sleeveLengthInput:SetWheelInc(0.001)
    layout:AddChild(self.sleeveLengthInput, LM.GUI.ALIGN_LEFT, 0)

end

function AM_Create_Limb_V2:UpdateWidgets(moho)

	if self.baseBodypart then

		if self.makeTangentForm then
			

			AM_Create_Limb_V2.startRadiusInput:SetValue(self.startRadius)
			AM_Create_Limb_V2.endRadiusInput:SetValue(self.endRadius)
			AM_Create_Limb_V2.settingsForMenu:SetChecked(self.settingsForBaseBodypart, true)
		else
			AM_Create_Limb_V2.startRadiusInput:SetValue(self.startRadius)
			AM_Create_Limb_V2.endRadiusInput:SetValue(self.startRadius)
			AM_Create_Limb_V2.settingsForMenu:SetChecked(self.settingsForBaseBodypart, true)
		end

	elseif self.endOfForearmShin then
		
		if self.makeTangentForm2 then
			AM_Create_Limb_V2.startRadiusInput:SetValue(self.startRadius2)
			AM_Create_Limb_V2.endRadiusInput:SetValue(self.endRadius2)
			AM_Create_Limb_V2.settingsForMenu:SetChecked(self.settingsForForearmShin, true)			
		else
			AM_Create_Limb_V2.startRadiusInput:SetValue(self.startRadius2)
			AM_Create_Limb_V2.endRadiusInput:SetValue(self.startRadius2)
			AM_Create_Limb_V2.settingsForMenu:SetChecked(self.settingsForForearmShin, true)
		end

	end
	
	AM_Create_Limb_V2.bodyPartNameInput:SetValue(self.bodyPartName)
	AM_Create_Limb_V2.strokeCheckbox:SetValue(self.stroke)
	AM_Create_Limb_V2.fillCheckbox:SetValue(self.fill)
	AM_Create_Limb_V2.startJointCheckbox:SetValue(self.startJoint)
	AM_Create_Limb_V2.endJointCheckbox:SetValue(self.endJoint)

	if self.endOfForearmShin then
		self.endJointCheckbox:Enable(false)
	else 
		self.endJointCheckbox:Enable(true)
	end

	if self.baseBodypart then
		AM_Create_Limb_V2.makeTangentFormCheckbox:SetValue(self.makeTangentForm)
	elseif self.endOfForearmShin then
		AM_Create_Limb_V2.makeTangentFormCheckbox:SetValue(self.makeTangentForm2)
	end

	AM_Create_Limb_V2.sleeveLengthInput:SetValue(self.sleeveLength)

	if self.endOfForearmShin then
		self.sleeveLengthInput:Enable(true)
	else 
		self.sleeveLengthInput:Enable(false)
	end

end

function AM_Create_Limb_V2:HandleMessage(moho, view, msg)

	if msg == self.CREATE_LIMB then -- CREATE LIMB BUTTON

		if (curFrame ~= frame0) then moho:SetCurFrame(frame0) end

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

		local boneLayer = moho:LayerAsBone(moho.layer)
		local skel = moho:Skeleton()
		for iBone = 0, skel:CountBones()-1  do

			local bone = skel:Bone(iBone)
			
			if (bone.fSelected) then
				local boneparent = skel:Bone(bone.fAnimParent:GetValue())
				
				if boneparent and boneparent.fSelected then
					self.startJointTest = false
				else
					self.startJointTest = true
				end

				local boneLength = bone.fLength
				bone.fStrength = 0

				-- CREATE START BONE
				local boneStart = skel:AddBone(0)
				self:ConstructionBoneSettings(moho, boneStart, iBone, 0, self.startRadius, ' start')
				local boneStartGlobalPose = self:GetGlobalBonePos(moho, boneStart)
				local boneLengthPos = LM.Vector2:new_local()
				local boneStartParentID = skel:BoneID(boneStart)
				boneLengthPos.x = boneStartGlobalPose.x + boneLength
				boneLengthPos.y = boneStartGlobalPose.y

				-- CREATE END BONE FOR MAIN SHAPE
				local boneEnd = skel:AddBone(0)
				local boneEndPos = LM.Vector2:new_local()
				boneEndPos:Set(boneLength*self.sleeve, 0)
				self:ConstructionBoneSettings(moho, boneEnd, iBone, 180, self.endRadius, ' end')

				if not self.makeTangentForm then
					boneEnd.fLength = self.startRadius
				end

				if self.endOfForearmShin then
					boneEnd.fAnimAngle:SetValue(0, math.rad(90))
					boneEnd.fAnimPos:SetValue(0, boneEndPos)
				else
					boneEnd.fAnimPos:SetValue(0, boneEndPos)
				end

				boneEnd.fAnimPos:SetValue(0, boneEndPos)
				local boneEndGlobalPose = self:GetGlobalBonePos(moho, boneEnd)
				local boneEndParentID = skel:BoneID(boneEnd)

				-- CREATE WRIST BONE

				if self.endOfForearmShin then
					local boneWrist = skel:AddBone(0)
					local boneWristPos = LM.Vector2:new_local()
					boneWristPos:Set(boneLength, 0)
					self:ConstructionBoneSettings(moho, boneWrist, iBone, 180, self.endRadius2, ' wrist')
					boneWrist.fAnimPos:SetValue(0, boneWristPos)

					self.boneWristGlobalPose = self:GetGlobalBonePos(moho, boneWrist)
					self.boneWristParentID = skel:BoneID(boneWrist)
				end
		
				local boneGlobalAngle = self:GetGlobalAngle(moho, boneStartGlobalPose, boneEndGlobalPose)
				
				local startBoneVec = LM.Vector2:new_local()
				local startBoneVec2 = LM.Vector2:new_local()

				startBoneVec:Set(0, 0)
				bone.fMovedMatrix:Transform(startBoneVec)
				startBoneVec.x = startBoneVec.x - self.startRadius
				
				startBoneVec2:Set(0, 0)
				bone.fMovedMatrix:Transform(startBoneVec2)
				startBoneVec2.x = startBoneVec2.x - self.startRadius2

				local bodypartLayer = moho:LayerAsVector(moho:CreateNewLayer(MOHO.LT_VECTOR, false))
				self:ShowConstructionCurvesTest(moho, bodypartLayer)
				moho:PlaceLayerInGroup(bodypartLayer, boneLayer, true, false)
				bodypartLayer:SetName(self.bodyPartName)
				local bodypartMesh = bodypartLayer:Mesh()

				if self.endOfForearmShin then
					-- DRAW WRIST PART --

					if self.startJoint and self.startJointTest then
						self:DrawCircle(bodypartMesh, boneStartGlobalPose, self.startRadius2, boneStartParentID)
						self:CreateBodypartShape(moho, bodypartMesh)
					end

					if self.makeTangentForm2 then
						self:DrawCircle(bodypartMesh, self.boneWristGlobalPose, self.endRadius2, self.boneWristParentID)
					else
						self:DrawCircle(bodypartMesh, self.boneWristGlobalPose, self.startRadius2, self.boneWristParentID)
					end

					self:CreateBodypartShape(moho, bodypartMesh)

					if self.makeTangentForm2 then

						self:DrawBodyPart(bodypartMesh, startBoneVec2, self.boneWristGlobalPose, self.startRadius2, self.endRadius2, boneStartParentID, self.boneWristParentID)
					else

						self:DrawBodyPart(bodypartMesh, startBoneVec2, self.boneWristGlobalPose, self.startRadius2, self.startRadius2, boneStartParentID, self.boneWristParentID)
					end

					local curveNumber2 = bodypartMesh:CountCurves() - 1
					local bodypartCurve2 = bodypartMesh:Curve(curveNumber2)
					self:CreateBodypartShape(moho, bodypartMesh)
					bodypartCurve2:SetSegmentOn(0, false)
					bodypartCurve2:SetSegmentOn(2, false)

					-- DRAW REST PART --

					if self.startJoint and self.startJointTest then
						boneStartGlobalPose.x = boneStartGlobalPose.x + self.startRadius2
						self:DrawCircle(bodypartMesh, boneStartGlobalPose, self.startRadius, boneStartParentID)
						self:CreateBodypartShape(moho, bodypartMesh)
					end
				
					if self.makeTangentForm then
						boneEndGlobalPose.x = boneEndGlobalPose.x - self.endRadius
					else
						boneEndGlobalPose.x = boneEndGlobalPose.x - self.startRadius
					end

					if self.makeTangentForm then

						self:DrawBodyPart(bodypartMesh, startBoneVec, boneEndGlobalPose, self.startRadius, self.endRadius, boneStartParentID, boneEndParentID)
					else

						self:DrawBodyPart(bodypartMesh, startBoneVec, boneEndGlobalPose, self.startRadius, self.startRadius, boneStartParentID, boneEndParentID)
					end

					self:CreateBodypartShape(moho, bodypartMesh)

					local curveNumber = bodypartMesh:CountCurves() - 1
					local bodypartcurve = bodypartMesh:Curve(curveNumber)
					bodypartcurve:SetSegmentOn(0, false)

				elseif self.baseBodypart then

					if self.startJoint and self.startJointTest then
						
						self:DrawCircle(bodypartMesh, boneStartGlobalPose, self.startRadius, boneStartParentID)
						self:CreateBodypartShape(moho, bodypartMesh)
					end

					if self.endJoint then
						if self.makeTangentForm then
							self:DrawCircle(bodypartMesh, boneEndGlobalPose, self.endRadius, boneEndParentID)
							self:CreateBodypartShape(moho, bodypartMesh)
							boneEndGlobalPose.x = boneEndGlobalPose.x + self.endRadius
						else
							self:DrawCircle(bodypartMesh, boneEndGlobalPose, self.startRadius, boneEndParentID)
							self:CreateBodypartShape(moho, bodypartMesh)
							boneEndGlobalPose.x = boneEndGlobalPose.x + self.startRadius
						end
					end

					if self.makeTangentForm then
						boneEndGlobalPose.x = boneEndGlobalPose.x - self.endRadius
					else
						boneEndGlobalPose.x = boneEndGlobalPose.x - self.startRadius
					end

					if self.makeTangentForm then
						self:DrawBodyPart(bodypartMesh, startBoneVec, boneEndGlobalPose, self.startRadius, self.endRadius, boneStartParentID, boneEndParentID)
					else
						self:DrawBodyPart(bodypartMesh, startBoneVec, boneEndGlobalPose, self.startRadius, self.startRadius, boneStartParentID, boneEndParentID)
					end
					self:CreateBodypartShape(moho, bodypartMesh)

					local curveNumber = bodypartMesh:CountCurves() - 1
					local bodypartcurve = bodypartMesh:Curve(curveNumber)
					bodypartcurve:SetSegmentOn(0, false)

					if self.endJoint then
						bodypartcurve:SetSegmentOn(2, false)
					end

				end

			end

			moho:SetSelLayer(boneLayer, false, true)
			moho:UpdateUI()
			moho.layer:UpdateCurFrame()

		end
	
	elseif msg == self.BASE_BODYPART then
		self.baseBodypart = true
		self.endOfForearmShin = false
		self.sleeve = self.sleeveDefault
		self:UpdateWidgets(moho)
	elseif msg == self.END_OF_FOREARMSHIN then
		self.baseBodypart = false
		self.endOfForearmShin = true
		self.sleeve = self.sleeveLength
		self:UpdateWidgets(moho)
	elseif msg == self.START_RADIUS then

		if self.baseBodypart then

			self.startRadius = self.startRadiusInput:Value()
			if self.magnitude < 0 - self.startRadiusInput:Value() then
				self.magnitude = 0 - self.startRadiusInput:Value()
			end

			self.endRadius = self.startRadius + self.magnitude
		elseif self.endOfForearmShin then
			self.startRadius2 = self.startRadiusInput:Value()

			if self.magnitude2 < 0 - self.startRadiusInput:Value() then
				self.magnitude2 = 0 - self.startRadiusInput:Value()
			end

			self.endRadius2 = self.startRadius2 + self.magnitude2
		end
		self:UpdateWidgets(moho)
	elseif msg == self.END_RADIUS then -- END RADIUS

		if self.makeTangentForm then
		
			if self.baseBodypart then
				self.endRadius = self.endRadiusInput:Value()
				self.magnitude = self.endRadiusInput:Value()-self.startRadius
			elseif self.endOfForearmShin then
				self.endRadius2 = self.endRadiusInput:Value()
				self.magnitude2 = self.endRadiusInput:Value()-self.startRadius2
			end

		else
			if self.baseBodypart then
				self.endRadius = self.endRadiusInput:Value()
				self.magnitude = 0
				self.startRadius = self.endRadiusInput:Value()
			elseif self.endOfForearmShin then
				self.endRadius2 = self.endRadiusInput:Value()
				self.magnitude2 = 0
				self.startRadius2 = self.endRadiusInput:Value()
			end
			
		end

		self:UpdateWidgets(moho)
	elseif msg == self.BODYPART_NAME then -- BODYPART NAME
		self.bodyPartName = self.bodyPartNameInput:Value()
	elseif msg == self.STROKE then -- STROKE
		self.stroke = self.strokeCheckbox:Value()
	elseif msg == self.FILL then -- FILL
		self.fill = self.fillCheckbox:Value()
	elseif msg == self.READ_RADIUS then -- READ RADIUS
		local skel = moho:Skeleton()

		for i = 0, skel:CountBones() - 1 do
			local bone = skel:Bone(i)

			if bone.fSelected then
				self.startRadius = bone.fLength
				self.magnitude = 0
				self.endRadius = bone.fLength
				break
			end

		end
		
	elseif msg == self.START_JOINT then -- START JOINT
		self.startJoint = self.startJointCheckbox:Value()
	elseif msg == self.END_JOINT then -- END JOINT
		self.endJoint = self.endJointCheckbox:Value()
	elseif msg == self.MAKE_TANGENT_FORM then -- MAKE TANGENT FORM

		if self.baseBodypart then
			self.makeTangentForm = self.makeTangentFormCheckbox:Value()
		elseif self.endOfForearmShin then
			self.makeTangentForm2 = self.makeTangentFormCheckbox:Value()
			self.magnitude2 = 0
		end
		
		self:UpdateWidgets(moho)
	elseif msg == self.SLEEVE_LENGTH then -- SLEEVE LENGTH
        self.sleeveLength = self.sleeveLengthInput:Value()
		self.sleeve = self.sleeveLength
	else
		
	end
	
end

Icon
Create Limb 2
Listed

Script type: Tool

Uploaded: Nov 08 2022, 08:08

Like Create Limb, but better!
Video instruction is coming soon

The previous version is here.
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: 698