I want to draw a line on the XZ plane that is snapped to angle increments. I’ve found the snapped angle, but I’m having trouble translating that back to the X and Z coordinates. Here’s my code:

local snapAngle = math.rad(studioService.RotateIncrement)
local vector = position - startPosition
local angle = math.atan2(vector.Z, vector.X)
local snappedAngle = math.floor(angle / snapAngle + 0.5) * snapAngle
local r = vector.Magnitude
local x = r * math.cos(snappedAngle)
local z = r * math.sin(snappedAngle)
local angleSnappedPosition = Vector3.new(x, position.Y, z)

In the above code I’m trying to calculate the snapped x and z values based on this snippet from the Wikipedia article on atan2:

The snappedAngle is correct, but the snapped x and z values are all over the place. Sometimes positive x and z values are getting translated to negative values.

I think the code is working correctly, that meaning that x and z values are going to give the correct values in the circle depending on the angle. However, you have not given an initial orientation that those coordinates should be relative to. Usually, the forward vector is Vector3.new(0, 0, -1), so maybe setting the angleSnappedPosition to:

Thanks. I am confident I can get it working if I use CFrames, but I’ve got no rotations or need to translate between coordinate systems so I’m trying to keep everything in pure trig. I’d really like to clear up my misunderstanding of the x = r * cos(angle) equation.

BTW the angles are referenced off the positive x axis:

Then in that case, the math looks correct. There shouldn’t be any problems with your code unless something else that you did not show is causing the problem. I went ahead and replicated this in an empty baseplate, and everything seems to be working fine.

local origin = Vector3.new(0, 0.5, 0)
local target = workspace.Target
local line = Instance.new("Part", workspace)
line.CanCollide = false
line.Anchored = true
local angleIncrement = 22.5
while wait() do
local snapAngle = math.rad(angleIncrement)
local vector = target.Position - origin
local angle = math.atan2(vector.Z, vector.X)
local snappedAngle = math.floor(angle / snapAngle + 0.5) * snapAngle
local r = vector.Magnitude
local x = r * math.cos(snappedAngle)
local z = r * math.sin(snappedAngle)
line.Size = Vector3.new(0.1, 0.1, r)
line.CFrame = CFrame.new(Vector3.new(x * 0.5, 0.5, z * 0.5), Vector3.new(x, 0.5, z))
end