defmodule Drone.Geometry do
@moduledoc false
# Shared position math for translating directional movement commands into
# x/y/z deltas, accounting for the drone's current yaw. Used by both the
# simulator adapter and the vehicle state tracker so their position models
# stay in sync.
@doc """
Returns the `{dx, dy, dz}` delta for a movement command.
`direction` is one of `:up`, `:down`, `:forward`, `:back`, `:left`, `:right`.
`distance` is in centimeters and `yaw` is the current heading in degrees.
"""
@spec move_delta(atom(), integer(), integer()) :: {integer(), integer(), integer()}
def move_delta(:up, distance, _yaw), do: {0, 0, distance}
def move_delta(:down, distance, _yaw), do: {0, 0, -distance}
def move_delta(:forward, distance, yaw), do: forward_delta(distance, yaw)
def move_delta(:back, distance, yaw), do: forward_delta(-distance, yaw)
def move_delta(:left, distance, yaw), do: right_delta(-distance, yaw)
def move_delta(:right, distance, yaw), do: right_delta(distance, yaw)
@doc """
Returns the `{dx, dy}` horizontal delta for a flip in the given direction.
"""
@spec flip_delta(atom()) :: {integer(), integer()}
def flip_delta(:left), do: {-20, 0}
def flip_delta(:right), do: {20, 0}
def flip_delta(:forward), do: {0, 20}
def flip_delta(:back), do: {0, -20}
@doc """
Returns the new yaw after rotating `degrees` in the given direction.
"""
@spec rotate_yaw(:cw | :ccw, integer(), integer()) :: integer()
def rotate_yaw(:cw, yaw, degrees), do: rem(yaw + degrees, 360)
def rotate_yaw(:ccw, yaw, degrees), do: rem(yaw - degrees + 360, 360)
defp forward_delta(distance, yaw) do
radians = yaw * :math.pi() / 180
{trunc(distance * :math.sin(radians)), trunc(distance * :math.cos(radians)), 0}
end
defp right_delta(distance, yaw) do
radians = yaw * :math.pi() / 180
{trunc(distance * :math.cos(radians)), trunc(-distance * :math.sin(radians)), 0}
end
end