How to find twist limits of a ball socket constraint via CFrames

So given two attachments, how do I find the angle of twist between them similar to a ball socket constraint using CFrames:

Screenshot 2021-01-07 at 6.34.51 PM

I know for the UpperAngle it’s dependent on the attachments world axis between the joint and the axis attachment and hence I can enforce a CFrame based constraint like so:

UpperAngle constraint
	local centerAxis = axisAttachment.WorldAxis
	local currentCenterAxis = jointAttachment.WorldAxis 
	local angleDifference = VectorUtil.AngleBetween(currentCenterAxis,centerAxis)

	local constraintUpperAngle = math.rad(jointConstraintInfo.UpperAngle) or math.rad(45)

	--out of bounds constrain it to world axis of the socket
	if angleDifference > constraintUpperAngle then
		local axis = currentCenterAxis:Cross(centerAxis)
		local angleDifference = angleDifference-constraintUpperAngle
		local newCenterAxisWithinBounds = rotateVectorAround( currentCenterAxis, angleDifference, axis )
		self.rotateJointFromTo(motor6d,currentCenterAxis,newCenterAxisWithinBounds,part0CF.RightVector)
	end

Now I’m completely stumped about how to find the TwistUpperAngle and TwistLowerAngle, I’ve tried using swing twist decomposition but tbh I don’t really understand if the angle I’m getting is the same and it doesn’t work for limiting the movement of the Motor6D part1 leg.

Failed attempt
	--local twistAxis = axisAttachment.WorldSecondaryAxis
	local twistAxis = jointAttachment.WorldAxis
	local axisCFrame = axisAttachment.WorldCFrame
	local jointCFrame = jointAttachment.WorldCFrame
	local jointRelativeToAxis = axisCFrame:ToObjectSpace(jointCFrame)
	local function swingTwist(cf, direction)
		local axis, theta = cf:ToAxisAngle()
		-- convert to quaternion
		local w, v = math.cos(theta/2),  math.sin(theta/2)*axis
	
		-- (v . d)*d, plug into CFrame quaternion constructor with w it will solve rest for us
		local proj = v:Dot(direction)*direction
		local twist = CFrame.new(0, 0, 0, proj.x, proj.y, proj.z, w)
		
		-- cf = swing * twist, thus...
		local swing = cf * twist:Inverse()
		
		return swing, twist
	end
	local swing,twist = swingTwist(jointRelativeToAxis,twistAxis)
	local x,y,z = twist:ToEulerAnglesXYZ()
	--print(math.round(math.deg(x)),math.round(math.deg(y)),math.round(math.deg(z)))-- it's the y axis?
	local constrainedX = math.clamp(math.deg(x),-45,45)
	local newTwistCF = CFrame.fromEulerAnglesXYZ(constrainedX,y,z)
	local newJointCFrameRelativeToWorld = axisCFrame*swing*newTwistCF
	--Translate jointCFrame to part1 CFrame
	local newPart1CFrame = newJointCFrameRelativeToWorld*jointAttachment.CFrame:Inverse() -- Uhh only works with attachments
	local goalCFRotation = motor6d.Part0.CFrame:Inverse()*newPart1CFrame
	goalCFRotation = goalCFRotation-goalCFRotation.Position

So anyone has any idea how the TwistLimits in the ball socket constraint work? What axis of the attachments is the limits based on?

2 Likes

NVM, solved in the latest GitHub version using the twist-swing deconstructor function thanks to @EgoMoose yet again, The twist limit is actually relative the Attachment0 WorldAxis as it’s swingTwist axis, and it’s CFrame. Will update the resource page soon after I write up the documentation :stuck_out_tongue:

Speen:

No Speen:

1 Like