Rotating a Part from it's Edge rather than the Center

Hi. I’m trying to replicate a feature from the plugin BuildV4, where a part rotates from an edge on another part to create smooth terrain:

I found this post

which helped me write this code, this is in an ArcHandle drag event, and edgePosition is the position of the edge to rotate around:

local normalEdges = getNormalEdges(hitPart, normal)
local originalCFrame = newPart.CFrame
local currentAngle = nil
local releaseAngle = 0
local dragConnection = handle.MouseDrag:Connect(function(axis, angle, radius)
	

	local angle = math.deg(angle) - (math.deg(angle) % rotationIncrement)
	angle = releaseAngle + angle
	if (angle > 360) then angle -= 360 elseif (angle < -360) then angle += 360 end
	local radAngle = math.rad(angle)
	
	-- Determine edge to rotate around
	local edgePosition
	if (axis == Enum.Axis.Top or axis == Enum.Axis.Bottom) then
		if ((0 < angle and angle < 180) or (-360 < angle and angle < -180)) then
			edgePosition = normalEdges.RightEdge
		else
			edgePosition = normalEdges.LeftEdge
		end
	else
		if ((0 < angle and angle < 180) or (-360 < angle and angle < -180)) then
			edgePosition = normalEdges.DownEdge
		else
			edgePosition = normalEdges.UpEdge
		end
	end
	
	-- Rotate part around edge.
	local hingeOffset = CFrame.new(edgePosition - originalCFrame.Position)
	local inverseHingeOffset = hingeOffset:Inverse()
	local baseCFrame = originalCFrame * hingeOffset
	local axis = Vector3.FromAxis(axis)
	local newCFrame = baseCFrame * CFrame.Angles(axis.X * radAngle, axis.Y * radAngle, axis.Z * radAngle) * inverseHingeOffset
	newPart.CFrame = newCFrame
	
	currentAngle = angle
	
end)
	
local releaseConnection = handle.MouseButton1Up:Connect(function(axis)
	releaseAngle = currentAngle
end)

This results in this behaviour.

The green points are where the edges are. As you can see, this works fine on non-rotated parts, but as soon as the original CFrame is rotated, this breaks.
Edit: After slowing down the CFrame calculations, it seems that when the original CFrame is rotated, doing originalCFrame * hingeOffset just results in a weird, offset position.

No ideas where to start on this, any help would be appreciated.

Fixed by doing

local hingeOffset = originalCFrame:toObjectSpace(CFrame.new(edgePosition))

instead of

local hingeOffset = CFrame.new(edgePosition - originalCFrame.Position)
1 Like