How would I "Snap" a CFrame to another with a 90-degree step?

Hello, I am in the process of writing a system that lets you snap various building pieces together when I came upon an interesting problem.

I’m trying to figure out how to “snap” two parts together while preserving the orientation to the nearest 90 degrees of a specific face. This is a vague and abstract explanation, so let me try to do explain it visually:

Imagine two CFrames, represented by these two parts. (The arrow doesn’t indicate movement or any functionality, it’s simply there as a show of the red part’s orientation)
image

Before “snapping” these two parts, there is typically going to be some angular difference, such as here.
image

In this case, snapping should produce this result: Note how the arrow’s overall orientation hasn’t changed relative to the “face” it ended up on.
image

So far, it should be simple enough. In the example above, it’s a one-dimensional rotation. But where things get difficult is when you have an offset like this, involving multiple dimensions of rotation:
image

In this case, the goal would be to rotate it such that it ends up like this: (the arrow is pointing to the right this time because the previous offset’s arrow is the closest to this result)
image

This behavior to be active for all faces of the blue part, and the complete system would only match the nearest face of the red part to the nearest relative face of the blue part, if that makes sense.

I’ve tried previously to solve this with some trial-and-error with dot and cross products but never managed to crack it on my own. I hope my explanation is enough to get my curiosity across. If any of you are familiar with this problem, can point me towards an explanation for it, or in general have any tips, tricks, or mathematical know-how that could be useful, I would be very grateful!

Alright, I made some big progress!
Using some intuition I’m picking up from linear algebra and another session of trial and error, this is what I have so far:

The principle, I think, is this:
What we’re essentially doing is projecting three dimensions of rotation down to one. To do this, I picked two of Part 1’s “basis vectors” (upVector and rightVector in this case, but I believe they are arbitrary as long as they are orthonormal).

Here’s my code:

Code

Test module:

local module = {}

-- Takes a normalized vector and figures out where the closest basis vector is in a cframe.
function module:GetClosestNormal(v3, cf)
	local a = math.huge
	local v = cf.lookVector	-- Arbitrarily chosen in case of worst-case
	
	local compAngles = {cf.lookVector, cf.upVector, cf.rightVector, -cf.lookVector, -cf.upVector, -cf.rightVector}
	for _, newV in pairs(compAngles) do
		local newA = math.acos(v3:Dot(newV)/(v3.Magnitude * newV.Magnitude))
		if newA < a then
			a = newA
			v = newV
			
			if newA == 0 then
				break
			end
		end
	end
	
	return a, v
end

-- Aligns two CFrames by a cross product and a given basis vector property name of the first cframe
-- Returns the translation that it takes to get to this point
function module:AlignCFramesByCross(cf1, cf2, property)
	local basis = cf1[property]
	local angle, closestBasis = module:GetClosestNormal(basis, cf2)
	local newCf = nil
	
	if angle > 0 then
		local cross = basis:Cross(closestBasis)
		newCf = CFrame.fromAxisAngle(cross, angle)
	else
		newCf = CFrame.new()
	end
	
	return newCf, angle
end

-- Takes 2 cframes and snaps them such that the nearest faces align, and the orientation of the second one locks to a 90 deg step
-- Cf1 is the CFrame attempting to snap, Cf2 is the existing one.
function module:GetFacedSnapOrientation(cf1, cf2)
	local t1, a1 = module:AlignCFramesByCross(cf1, cf2, 'UpVector')
	local t2, a2 = module:AlignCFramesByCross(t1 * cf1, cf2, 'RightVector')
	
	return t2 * t1 * cf1
end

return module

Test script:

local alignModule = require(script.ModuleScript)
local test1 = game.Workspace.Test1

while wait() do
	local baseCf = test1.Moveable.CFrame
	local newCf = alignModule:GetFacedSnapOrientation(baseCf, test1.Align.CFrame)
	test1.Test.CFrame = newCf - newCf.p + test1.Align.CFrame.p
end

So now I’m curious how I could quantify the whole rotation as a single value, like some kind of “rotational magnitude” so I can compare multiple snapping attempts and figure out which one is more reasonable if there are multiple ones competing.