Help with a little math in lua

I have a Square and a circle. With the crank you can rotate the square around the circle. With the arrows you can move the circle.

But when you move the circle in some direction, the Square is also moving. I do not get why it is doing this. I have to admit I am not great at math. But my understanding was that in the update loop the x and y values of the circle will be gotten from that current position. If the circle is moved - the square should just rotate relative to that position and not move along with it.

Anyone has any idea what I am overlooking or doing wrong?

Below is the full code from main.lua:

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"


local pd <const> = playdate
local gfx <const> = pd.graphics

local circle = nil
local square = nil

local function init()

	-- Square
	local fillRect = gfx.image.new(100, 100)
	square = gfx.sprite.new(fillRect)
    gfx.pushContext(fillRect)
    gfx.fillRect(0, 0, 100, 100)
    gfx.popContext()
    square:setImage(fillRect)
	square:moveTo(200, 50)
	square:add()
 
	-- Circle
	local r = 10
	local circleImage = gfx.image.new(r * 2, r * 2)
	circle = gfx.sprite.new(circleImage)
    gfx.pushContext(circleImage)
    gfx.fillCircleAtPoint(r, r, r)
    gfx.popContext()
	circle:setCollideRect(0, 0, r * 2, r * 2)
    circle:setImage(circleImage)
	circle:moveTo(200, 120)
	circle:add()
end

init()

function pd.update()
	
	local angle = pd.getCrankPosition()

	-- ROTATION of square around circle
	local a = square.x - circle.x
	local b = square.y - circle.y
	local r = math.sqrt( a * a + b * b )
	local x = circle.x + r * math.cos( math.rad(angle) )
	local y = circle.y + r * math.sin( math.rad(angle) )
	square:moveTo(x, y)
	square:setRotation(angle)

	-- Circle move
	if pd.buttonIsPressed('up') then
		circle:moveBy(0,-2)
	end
	if pd.buttonIsPressed('right') then
		circle:moveBy(2,0)
	end
	if pd.buttonIsPressed('down') then
		circle:moveBy(0,2)
	end
	if pd.buttonIsPressed('left') then
		circle:moveBy(-2,0)
	end
 
	gfx.sprite.update()
end

Based on the code, the square is supposed to rotate around the circle based on the crank's angle, and the circle can be moved using the arrow buttons. The issue you're facing is that when the circle moves, the square also moves in the same direction.

The problem is with how you're calculating the new position of the square relative to the circle after moving the circle. Specifically, you're calculating the distance r and then using it to find new coordinates for the square.

Let's dissect the code snippet responsible for this:

local a = square.x - circle.x
local b = square.y - circle.y
local r = math.sqrt( a * a + b * b )
local x = circle.x + r * math.cos( math.rad(angle) )
local y = circle.y + r * math.sin( math.rad(angle) )
square:moveTo(x, y)

Here's the issue:

  1. When you move the circle, you're not updating square.x and square.y to be relative to the new circle position. Instead, you're using the old square.x and square.y to calculate r, the distance between them.

So, every time you move the circle and update circle.x and circle.y, you also need to update square.x and square.y so that they are consistent with the new position of the circle.

One way to fix this is to maintain a constant distance r and angle that should be used to set the square's position relative to the circle whenever the circle moves or the crank is turned.

Here's a revised version of your pd.update() function to illustrate this idea:

-- initialize 'r' and 'angle' outside of pd.update so they persist
local r = 70 -- initial distance between circle and square, you can change this
local angle = 0

function pd.update()
	
	angle = pd.getCrankPosition() -- Update the angle based on the crank position

	-- ROTATION of square around circle
	local x = circle.x + r * math.cos( math.rad(angle) )
	local y = circle.y + r * math.sin( math.rad(angle) )
	square:moveTo(x, y)
	square:setRotation(angle)

	-- Circle move
	if pd.buttonIsPressed('up') then
		circle:moveBy(0,-2)
	end
	if pd.buttonIsPressed('right') then
		circle:moveBy(2,0)
	end
	if pd.buttonIsPressed('down') then
		circle:moveBy(0,2)
	end
	if pd.buttonIsPressed('left') then
		circle:moveBy(-2,0)
	end
 
	gfx.sprite.update()
end

By taking this approach, the square will maintain its distance r from the circle, rotating around it based on the crank's angle, and will also properly follow the circle as it moves.

Thanks so much for your answer. This is great if you want the square to move along with the circle (while rotating around it with the crank)

But actually I want the square to move around the circle (with the crank). But it should NOT move along with the circle. The circle should move independent of the square with the arrows. If you get closer to the square - than the square has a smaller rotation around the circle and if you get further away the rotation is bigger. That is how it is supposed to work. But somehow the square moves with the circle if you press the arrows. And not even really consistent I might add.

Your current code expects the angle between the square and the circle to always be the angle of the crank. However, if the circle moves, the angle between the circle and the square will change.

I think it will be easier if you use playdate.getCrankChange() instead of pd.getCrankPosition(). This way, you can change the square center only if the crank has been moved:

local crankChange = playdate.getCrankChange()
if crankChange != 0 then
	-- ROTATION of square around circle
	local a = square.x - circle.x
	local b = square.y - circle.y
	local r = math.sqrt(a * a + b * b)
	square:moveBy(r * math.cos(crankChange), r * math.sin(crankChange))
end
-- TODO: calculate the angle between the square and the circle to rotate the square

Thanks that works! I would prefer it if I could always use the crank angle, also when the crank did not just move. I am working on a driving game where you use the crank as a steering wheel. So that means objects need to keep rotating as long as the crank is rotated.