Working out Rotation in Degrees between 2 CFrames

Hey there devs.

I’m working on a rail placement system, but i have run into a road block and i don’t know what to do here.
When placing rails at the moment, it allows you to make extremely sharp curves, and what i am looking to do is prevent that, but denying placement if the curve is too sharp.

The problem is, i’m unsure exactly how to go about checking the rotation from the start of the rail to the end of the rail. Once i do that, the rail may be, say, 90 degrees, but it must require that the length of the track is say 150 studs.

What i have tried to do, is using the CFrame at the beginning point of the track and the CFrame at the end point of the track, then get the look vectors of them and work out the difference, but that is giving me a Vector3. I just need a value of the turn the track has made in degrees, say, a 37 degree turn.

Here is what it currently looks like:
https://gyazo.com/feadefe1717cd725e99ffbc41296e272

It is just another thing i need to restrict with the placement of the rails, like the length, which i have already done.

Thank you for any help,

  • Mezza

I have explained a common method to calculate the angle between two vectors in this post:

You can apply this to your case by using the LookVectors of your CFrames, as you mentioned you had been doing already.

2 Likes

Thanks for the help @sircfenner. Your solution does work, but there is just one problem.

At some rail lengths, it is returning -nan(ind) in the chat, when the rail is straight.
Would you know why?

Here is what is happening:
https://gyazo.com/cd13d1989b201315162da8b2023ddd0f

Can you share the relevant part of your code? It may be an implementation problem on your part, or an edge case that I haven’t considered.

Do you use inverse trig functions anywhere in your code? You need to keep on mind that due to floating point errors the arguments you pass to inverse trig functions may be slightly out of range (eg: Acos(1.0000165)), so you should be clamping any values you pass to inverse trig functions to the appropriate range explicitly.

1 Like

That might be the problem. I am using math.acos, and at some times, the number is just a little over or under what it should be. How would i go about fixing that?

Here is the relevant code:

    b = Bezier.new(unpack(allPoints))
	local lengt = b:GetLength()
	if lengt > maxlen then toolong = true else toolong = false end
	local curve = allPoints[1]
	local fa, ba = b:Get((0+1)/lengt), b:Get((0)/lengt)
	local fb, bb = b:Get((1)/lengt), b:Get((lengt-1)/lengt)
	local ac, bc = CFrame.new((fa+ba)/2, fa), CFrame.new((fb+bb)/2, fb)
	local fac = ac.LookVector
	local vec = (bc.Position - ac.Position).unit
	local angl = math.acos(fac:Dot(vec))
	print(angl)
	
	if angl/lengt > maxlen then
		toolong = true
	else
		toolong = false
	end

Your math is fine in this case, it’s just the floating point error getting you. In that case, just write a statement forcing it in range:

 math.max(-1, math.min(1, dot product result))

Thank you, that fixed it up.
Here is my updated code:

    b = Bezier.new(unpack(allPoints))
	local lengt = b:GetLength()
	if lengt > maxlen then toolong = true else toolong = false end
	local curve = allPoints[1]
	local fa, ba = b:Get((0+1)/lengt), b:Get((0)/lengt)
	local fb, bb = b:Get((1)/lengt), b:Get((lengt-1)/lengt)
	local ac, bc = CFrame.new((fa+ba)/2, fa), CFrame.new((fb+bb)/2, fb)
	local fac = ac.LookVector
	local vec = (bc.Position - ac.Position).unit
	local angl = math.acos(math.max(-1, math.min(1, fac:Dot(vec))))
	print(angl)
	if angl/lengt > maxrot then toosharp = true else toosharp = false end

It seems that this code is now giving me just the rotation of the track relative to the word, and not the rotation difference between the two points:

local a, b = allPoints[1], allPoints[#allPoints]
local fac = b.LookVector
local vec = (a.p - b.p).unit
local angl = math.deg(math.acos(math.max(-1, math.min(1, fac:Dot(vec)))))

Any idea what i have done wrong?