# Movement in the flow of cos and sin

### Code

``````local p = workspace.Part
local s, positive = 0, true

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
p.Rotation = p.Rotation:Lerp(p.Rotation + Vector3.new(math.cos(math.pi * 2 / 180 * s), math.sin(math.pi * 2 / 180 * s), 0), DeltaTime * 15)

if positive then
s += 1
if s == 180 then
s = 0
positive = false
end
else
s -= 1
if s == -180 then
s = 0
positive = true
end
end
end)
``````

Please explain why this is happening and what I need to do to fix it.

Use Quaternions:

``````local p = workspace.Part
local s, positive = 0, true

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
local rotationQuaternion = CFrame.Angles(0, rotationDelta, 0)
local currentRotation = p.CFrame
local newRotation = currentRotation * rotationQuaternion

p.CFrame = currentRotation:Lerp(newRotation, DeltaTime * 15)

if positive then
s = (s + 1) % 360
if s == 180 then
positive = false
end
else
s = (s - 1) % 360
if s == 0 then
positive = true
end
end
end)
``````

Previous:

Quaternion version:

Oops, itâ€™s supposed to be in the opposite direction. (drawing)

1 Like

Thanks for trying, but you didnâ€™t quite understand what I want. Everything works correctly in the main part of the script. I want the part to rotate along the x and y axes, and the transition between rotation to a negative and positive environment is carried out along a circular trajectory, instead of an infinite one.

If itâ€™s still not clear, look at how the addition of widgets on iOS is implemented. In the preview, they rotate slightly as if showing themselves from different sides and they do it along a circular trajectory.

I presume this is the effect youâ€™re going for?

Your s value (the degrees) is going from -180 to 180 which involves returning to the same value multiple times. In order to solve this, you shouldnâ€™t start decrementing s, since 360 degrees is the same as -360, -180 is the same as 180 degrees. Youâ€™re basically telling it to reverse everything it did already (and go backwards), which is what creates that infinity-like motion from earlier.

By going from 0 to 360, youâ€™re making sure itâ€™s always increasing the angle, and once it gets past 360, you set it to 0 so youâ€™re still in the space of [0, 360].

``````	p.Rotation = p.Rotation:Lerp(p.Rotation + Vector3.new(math.cos(math.rad(s)) , math.sin(math.rad(s)), 0), DeltaTime * 15)

s += 1

if s >= 360 then
s = 0
end
``````

(also you donâ€™t need to manually convert degrees into radians, thereâ€™s math.rad())
(there also doesnâ€™t seem to be a limit for x, on math.rad(x), but Iâ€™d reset it after 360 anyways)

1 Like

At first I thought so too, but the problem is that changing -180 and 180 by 360 increases the amplitude of rotation, and rotation will be carried out only by positive numbers. That is, it rotates only up and to the right, then returns to its original position. My problem is the specific transition from -180 to 180 and vice versa, that is, instead of continuing to rotate from the bottom and smoothly rise up, the part rises to the top and goes to the left. That is, the axis of its rotation itself is incorrect (as a symbol of infinity).

It seems that you thought that I meant that it should rotate only in +, although I want it to rotate in + and -, but the transition from these 2 positions was like a circle, not like infinity (axis of rotation).

I think thatâ€™s a problem of the initial rotation? If I start at 0,0,0 rotation (horizontal) it looks like this:

Iâ€™m not really sure how youâ€™d be able to get from where we are to your desired effect, sorry.

Maybe store the rotation values of each corner being pushed â€śdownâ€ť and lerp through them?

I tried making it work with quaternions. It should give the desired effect, like the video you sent in. You might need to put the quaternion class in a module script.

``````local Quaternion = {}
Quaternion.__index = Quaternion

function Quaternion.new(w, x, y, z)
local invLength = 1 / math.sqrt(w * w + x * x + y * y + z * z)

local self = setmetatable({
W = w * invLength,
X = x * invLength,
Y = y * invLength,
Z = z * invLength,
}, Quaternion)

return self
end

function Quaternion.fromAxisAngle(axis, angle)
local sin = math.sin(angle / 2)
return Quaternion.new(math.cos(angle / 2), sin * axis.X, sin * axis.Y, sin * axis.Z)
end

function Quaternion:Conjugate()
return Quaternion.new(self.W, -self.X, -self.Y, -self.Z)
end

function Quaternion.__mul(self, other)
local w = self.W * other.W - self.X * other.X - self.Y * other.Y - self.Z * other.Z
local x = self.X * other.W + self.W * other.X - self.Z * other.Y + self.Y * other.Z
local y = self.Y * other.W + self.Z * other.X + self.W * other.Y - self.X * other.Z
local z = self.Z * other.W - self.Y * other.X + self.X * other.Y + self.W * other.Z

return Quaternion.new(w, x, y, z)
end

function Quaternion:ToAxisAngle()
local angle = math.acos(self.W) * 2
local sin = math.sin(angle / 2)
local axis = Vector3.new(self.X, self.Y, self.Z) / sin
return axis, angle
end

function Quaternion:ToCFrame()
local nx = self * Quaternion.new(0, 1, 0, 0) * self:Conjugate()
local ny = self * Quaternion.new(0, 0, 1, 0) * self:Conjugate()
local nz = self * Quaternion.new(0, 0, 0, 1) * self:Conjugate()

nx = Vector3.new(nx.X, nx.Y, nx.Z)
ny = Vector3.new(ny.X, ny.Y, ny.Z)
nz = Vector3.new(nz.X, nz.Y, nz.Z)

return CFrame.fromMatrix(Vector3.zero, nx, ny, nz)
end

function Quaternion:Slerp(other, t)
local rel = other * self:Conjugate()

local axis, angle = rel:ToAxisAngle()
angle *= t
return Quaternion.fromAxisAngle(axis, angle)
end

local p = workspace.Part
local s, positive = 0, true

local origin = p.CFrame

local speedMultiplier = 100

game:GetService("RunService").PostSimulation:Connect(function(DeltaTime)
local rot = math.pi / 8

local identity = Quaternion.new(1, 0, 0, 0)

local up = Quaternion.new(math.cos(rot / 2), 0, 0, math.sin(rot / 2))
local right = Quaternion.new(math.cos(rot / 2), math.sin(rot / 2), 0, 0)