Please help with angle mathematics

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
OneOrigiN
Prole
Posts: 7
Joined: Fri Jul 24, 2020 3:04 pm

Please help with angle mathematics

Post by OneOrigiN » Mon Jan 11, 2021 12:27 pm

Hello everyone!
I have object "rimmyx".
In rimmyx there is: variable

Code: Select all

self.angle
and table

Code: Select all

self.main_angles
that stores angles.
Why I did this? I need to calculate a points where I need to draw a picure,
So for each point I calculate anlge:

Code: Select all

self.angle + self.main_angles[i]
(and then some geometry). PLEASE remember this!

Code: Select all

self.main_angles
stores 6 angle measures from 0 to 2 * PI (0, PI/3, (2*PI)/3, PI, and so on...).
Player can rotate rimmyx with mouse. In this case, only

Code: Select all

self.angle
is changing.
Also, I have a function

Code: Select all

self:findPart(_angle)
, which is supposed to return index of element in the table

Code: Select all

self.main_angles
, checking every element in

Code: Select all

self.main_angles
.
The problem is, My program crashes (sometimes), because of find function.
Exactly here:

Code: Select all

math.abs(_angle - (v[1] + self.angle))
(v[1] because of

Code: Select all

for i, v in ipairs(self.main_angles)
)
At some moment minimum

Code: Select all

math.abs(_angle - (v[1] + self.angle))
is bigger than 1(!)
So, how I need to write it correctly?
Some art(no):
love_forum_1.png
love_forum_1.png (1.04 MiB) Viewed 4984 times
Logs:
The attachment love_forum_1.png is no longer available
whole code:
(sorry for non-english comments)

Code: Select all

rimmyx = Object:extend()
require "part"
angle_measure = 0
function rimmyx:new(_x, _y, _len, _is_left, _name)
  angle_measure = 2 * math.pi / 6
  self.x = _x
  self.y = _y
  self.main_len = _len
  self.angle = 0
  self.secondary_len = _len / math.cos(angle_measure / 2) -- вторая длина. До ВТОРОГО ряда деталей.
  self.is_left = _is_left
  self.delta_angle = 0 -- разница между углом до позиции мыши, и нынешним углом, используется в фунциях mousePressed и mouseReleased
  self.target_angle = 0 -- куда нужно "довернуть колесо". Измеряется в радиантах
  self.mouse_is_down = false
  self.is_attached = nil
  self.is_stabilized = true
  self.attachment_index = nil
  self.name = _name
  -- creating a list of angles
  self.main_angles = {}
  self.secondary_angles = {}
  self.img_main_angles = {}
  self.img_secondary_angles = {}
  -- filling list of angles
  for i = 1,6 do
    self.main_angles[i] = {(i - 1) * angle_measure, part((i - 1) * angle_measure, "cube.png")}
    self.secondary_angles[i] = i * angle_measure - 1 * math.pi / 6
  end
  if not _is_left then
    self.main_angles[4][2] = nil
    self.is_attached = false
  else
    self.is_attached = true
  end
  --self.main_angles[1][2].angle = angle_measure * 4
end

function rimmyx:isAttached()
  return self.is_attached
end

function rimmyx:findPart(_angle)--ищем угол с угловым значением _angle(в радианах).
  print("trying to find angle: " .. _angle)
  local m = angle_measure * 2
  local ind = nil
  for i, v in ipairs(self.main_angles) do
    print("delta: " .. math.abs(_angle - (v[1] + self.angle)) .. " |self.angle, v[1], i|: " .. self.angle .. " " .. v[1] .. " " .. i)
    if math.abs(_angle - (v[1] + self.angle)) <= 0.1 then -- math.abs(_angle - (v[1] + self.angle)) <= 0.5 or math.abs(math.abs(_angle - (v[1] + self.angle)) - 2 * math.pi) <= 0.5
      ind = i
      m = _angle - (v[1] + self.angle)
      break
    end
  end
  print("result index: " .. ind)
  return ind
end

function rimmyx:popPart(_angle)
  print("going to pop: " .. _angle .. " from " .. self.name)
  local ind = self:findPart(_angle)
  local returnable_part = self.main_angles[ind][2]
  self.main_angles[ind][2] = nil
  print("popped from " .. ind)
  return returnable_part
end

function rimmyx:addPart(_part, _angle)
  print("going to add: " .. _angle .. " to " .. self.name)
  local ind = self:findPart(_angle)
  print("added to " .. ind)
  print("==============================================================================================")
  _part.angle = _part.angle - self.angle
  self.main_angles[ind][2] = _part
end

function rimmyx:addMainPart(_angle, _part)
  --for i, v in ipairs(self.main_angles)
end

function rimmyx:addSecondaryPart (_angle, _part)

end

function rimmyx:mousePressed(x, y, button, isTouch)
  self.delta_angle = math.atan2(y - self.y, x - self.x) - self.angle
  self.mouse_is_down = true

end

function rimmyx:mouseReleased()
  local a_angle = math.floor(self.angle / (2 * math.pi / 6))--находим 2 ближайших правильных угла поворота фигуры
  local b_angle = math.ceil(self.angle / (2 * math.pi / 6))
  self.is_stabilized = false
  if math.abs(self.angle - a_angle) > math.abs(self.angle - b_angle) then -- их них находим ближайший
    self.target_angle = b_angle * 2 * math.pi / 6
  else
    self.target_angle = a_angle * 2 * math.pi / 6
  end
  self.mouse_is_down = false
end

function rimmyx:update(dt)
  if self.mouse_is_down == true then
    mouse_x, mouse_y = love.mouse.getPosition()
    self.angle = math.atan2(mouse_y - self.y, mouse_x - self.x) - self.delta_angle
  else
    if math.abs(self.angle - self.target_angle) < 0.001 and self.is_stabilized == false then
      self.angle = self.target_angle
      self.is_stabilized = true
    else
      self.angle = self.angle - (self.angle - self.target_angle) / 50
      if self.angle >= 2 * math.pi then
        self.angle = self.angle % (2 * math.pi)
      end
    end
  end
end

function rimmyx:draw()
  love.graphics.setColor(0, 1, 0, 1)
  for i, v in ipairs(self.main_angles) do
    if v[2] ~= nil then
      local x, y = self.x + self.main_len * math.cos(v[1] + self.angle), self.y + self.main_len * math.sin(v[1] + self.angle)
      love.graphics.line(self.x, self.y, x, y)
      v[2]:changePosition(x, y)
      love.graphics.setColor(1, 1, 1, 1)
      v[2]:draw(self.angle)
      love.graphics.setColor(1, 0, 0, 1)
      love.graphics.print(v[2].angle .. " " .. (v[2].angle + self.angle) .. " "  .. (v[1] + self.angle) .. " " .. i , x, y)
      love.graphics.setColor(0, 1, 0, 1)
    end
  end
  love.graphics.setColor(1, 0, 0, 1)
  for i, v in ipairs(self.secondary_angles) do
    local x, y = self.x + self.secondary_len * math.cos(v + self.angle), self.y + self.secondary_len * math.sin(v + self.angle)
    love.graphics.line(self.x, self.y, x, y)
  end
end
Last edited by OneOrigiN on Tue Jan 12, 2021 10:56 am, edited 1 time in total.

Ross
Prole
Posts: 26
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Please help with angle mathematics

Post by Ross » Mon Jan 11, 2021 10:42 pm

Is this the console output your are getting before it fails?

Code: Select all

trying to find angle: 5.235987755983
delta: 5.235987755983 |self.angle, v[1], i|: 0 0 1
delta: 4.1887902047864 |self.angle, v[1], i|: 0 1.0471975511966 2
delta: 3.1415926535898 |self.angle, v[1], i|: 0 2.0943951023932 3
delta: 2.0943951023932 |self.angle, v[1], i|: 0 3.1415926535898 4
delta: 1.0471975511966 |self.angle, v[1], i|: 0 4.1887902047864 5
delta: 8.8817841970013e-016 |self.angle, v[1], i|: 0 5.235987755983 6
The problem is, you are trying to compare floating point numbers directly.
Generally speaking, you can't do math on decimal numbers and then compare them directly using `==`. The numbers will be a tiny bit imprecise, so the comparison will fail.

Look at the last delta:

Code: Select all

delta: 8.8817841970013e-016
It's not zero, it's 0.00000000000000088817841970013.

The simplest solution is to check if it's "roughly equal", rather than exactly equal ("=="). You can do:

Code: Select all

if delta < 0.0001
or something.

Another solution is to change how your `findPart` method works. Rather than searching through a list to find the matching index, you just need to round the angle to the nearest 1/6th of the circle.
Try this:

Code: Select all

local function round(x)
	return math.floor(x + 0.5)
end

function rimmyx:findPart(_angle)
	_angle = _angle % (math.pi * 2) -- Ensures the angle is inside a circle.
	local interval = math.pi / 3
	local offset = interval
	return round((_angle + offset) / interval)
end
Note: You probably want to change the variables. Maybe `offset` should be `self.angle`?

OneOrigiN
Prole
Posts: 7
Joined: Fri Jul 24, 2020 3:04 pm

Re: Please help with angle mathematics

Post by OneOrigiN » Tue Jan 12, 2021 10:55 am

meh, I just was experimenting with comparing numbers. In first version there was math.abs(_angle - (v[1] + self.angle))... and that still not working.
And sorry for variables. My vocabulary a little bit poor.

User avatar
darkfrei
Party member
Posts: 114
Joined: Sat Feb 08, 2020 11:09 pm

Re: Please help with angle mathematics

Post by darkfrei » Tue Jan 12, 2021 11:49 am

OneOrigiN wrote:
Tue Jan 12, 2021 10:55 am
meh, I just was experimenting with comparing numbers.
Remember that ((0.1+0.2)==0.3) is always false :)
https://medium.com/better-programming/w ... 432310d476

Ross
Prole
Posts: 26
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Please help with angle mathematics

Post by Ross » Tue Jan 12, 2021 2:05 pm

OneOrigiN wrote:
Tue Jan 12, 2021 10:55 am
And sorry for variables. My vocabulary a little bit poor.
Sorry, I meant the values of variables in my example code. You can name your variables whatever you want. :)

OneOrigiN
Prole
Posts: 7
Joined: Fri Jul 24, 2020 3:04 pm

Re: Please help with angle mathematics

Post by OneOrigiN » Tue Jan 12, 2021 4:36 pm

I think you don't quite understand what function need to do... Let me explain it second time.
In two words: I have a center of circle, some angle (of circle if you interested) and table of angles.
Try to draw some sort of wheel with 6 spokes - that's picture of what going on.
Every spoke has angle and index.(and it can't be changed)
Player can rotate wheel (on fixed angle, so there is always spoke that points right)
function findPart(_angle) need to return index of spoke which angle equals to _angle at the MOMENT(or not exactly equal, anyway you got it)
why at the MOMENT? Because player can rotate wheel. => at the start findPart(0) returns 1 (because spoke with i = 1 at the angle 0)
Then wheel rotates at 2*PI/3 findPart(0) returns 5 because spoke with i = 5 is at the angle 0.
And so on, and so on...

Ross
Prole
Posts: 26
Joined: Tue Mar 13, 2018 12:12 pm
Contact:

Re: Please help with angle mathematics

Post by Ross » Wed Jan 13, 2021 12:29 am

Yeah, I think I got it.

This is what you want, right?: https://i.imgur.com/rtKTxzP.mp4

The problem with your code that I saw was: You're trying to check if two numbers are exactly identical, but the numbers were not exactly equal, they were just very, very close, so it failed.
To fix that problem, change your code so it only uses >, <, >=, or <=, NOT ==.

I suggested a totally different, simpler way to do the same thing...
...but my code actually had some issues, :cry: sorry (I didn't check it with all angles...). This code actually works:

Code: Select all

function rimmyx:findPart(_angle)
	local interval = math.pi / 3
	_angle = (_angle + interval/2 - self.angle) % (math.pi*2)
	return math.floor(_angle / interval) + 1
end

OneOrigiN
Prole
Posts: 7
Joined: Fri Jul 24, 2020 3:04 pm

Re: Please help with angle mathematics

Post by OneOrigiN » Wed Jan 13, 2021 8:40 am

"This is what you want, right?: https://i.imgur.com/rtKTxzP.mp4" - woaaaaaaah, just exactly my prototype.
And about checking - if you noticed, I changed it:

Code: Select all

math.abs(_angle - (v[1] + self.angle)) <= 0.1

Post Reply

Who is online

Users browsing this forum: No registered users and 31 guests