Positional crank maths

So, I have a plane sprite with 24 angles. The planeRot value can thus be an integer in the range 0..23, inclusive.

I managed to translate the crank degrees to this 24-angle coordinate system: crankRot.

Now, I can easily check whether the plane should rotate to point in the same direction as the crank:

if planeRot == crankRot then
  -- nothing to do
else
  -- rotate plane left or right??
end

Given that the plane is allowed to rotate one step per frame, How to calculate whether the the plane should rotate counter-clockwise or clockwise?
To rephrase the question: how do I create an algorithm that would output -1 if crankRot can be reached from planeRot in fewer steps by rotating counter-clockwise that rotating clockwise?

Sample expected output:

CrankRot PlaneRot Desired Output Counter clockwise steps Clockwise steps
0 1 -1 0 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0
1 0 1 23 22 21 .. 1 1
23 0 -1 23 1 .. 23
0 23 1 22 .. 0 0
18 2 1 1 0 23 22 21 20 19 18 19 20 21 23 0 1 2
6 20 -1 5 4 3 2 1 0 23 22 21 20 7 8 9 10 11 12 13 14 14 16 17 18 19 20

I guess @matt might have solved this problem ages ago for Daily Driver :wink:
(or perhaps he doess not have that one-step-per-frame limitation and can simply call planeRot = crankRot)

1 Like

I believe I did this for Circular but I don't have my code to hand.

You may enjoy this stack overflow answer: https://gamedev.stackexchange.com/a/181952 (untested, but familiar)

theta = ((destAngle - startAngle) + 180) % 360 - 180;

Will return a signed angle in the range of [-180,180] so that you can get rotational direction along with the smallest distance.

1 Like

Excellent. Thanks for the Google-fu, these kind of problems are doable but hard to wrap your head around at the end of the day. Let alone feel confident that you have the most efficient solution. I do unittest, so I can feel confident that the implementation is correct, at least. I also tend to forget that the gamedev stackoverflow exists. So thanks!

for those interested, here is my little testing framework:

--- calculate the shortest distance to reach destRotation from startRotation
--- assumes a circle with 24 angles, so that angle 0 == angle 24 and the maximum angle is 23
--- example: smallestPlaneRotation(23, 1) => -2
function smallestPlaneRotation(destRotation, startRotation)
    return ((destRotation - startRotation) + 12) % 24 - 12
end

local function test(expected, actual, description)
    if expected ~= actual then
        print(description)
        error(string.format("Expected %s, got %s", expected, actual))
    end
end

local smallestPlaneRotation <const> = smallestPlaneRotation
local function smallestRotationTest(destRotation, startRotation, expected)
    test(expected, smallestPlaneRotation(destRotation,startRotation), table.concat({ "smallestRotation", destRotation, startRotation }, ", " ))
end

smallestRotationTest(6,5, 1)
smallestRotationTest(5,6, -1)
smallestRotationTest(23, 0 , -1)
smallestRotationTest(0,23, 1)
smallestRotationTest(1, 23, 2)
smallestRotationTest(23, 1, -2)
smallestRotationTest(0, 12, -12) -- equal distance, result sign doesn't matter
1 Like