defmodule Vector2D do
@moduledoc """
Documentation for `Vector 2D`.
"""
@doc """
Create a new two dimensional vector from given values
## Examples
iex> Vector2D.new(10, 1)
%{x: 10, y: 1}
iex> Vector2D.new()
%{x: 0, y: 0}
"""
@spec new(number, number) :: %{x: number, y: number}
def new(x \\ 0, y \\ 0), do: %{x: x, y: y}
@doc """
Returns vector length
### Two dimensional vector length chart

## Examples
iex> Vector2D.length(%{x: 2, y: 2})
2.8284271247461903
iex> Vector2D.new(-3, 3) |> Vector2D.length()
4.242640687119285
"""
@spec length(%{x: number, y: number}) :: float
def length(vector) do
:math.sqrt(:math.pow(vector[:x], 2) + :math.pow(vector[:y], 2))
end
@doc """
Increment one vector by another one
## Examples
iex> Vector2D.add(%{x: 2, y: 2}, %{x: 2, y: 2})
%{x: 4, y: 4}
iex> Vector2D.new(-3, 3) |> Vector2D.add(%{x: 3, y: 6})
%{x: 0, y: 9}
"""
@spec add(%{x: number, y: number}, %{x: number, y: number}) :: %{x: number, y: number}
def add(curr_vector, given_vector) do
%{
x: curr_vector[:x] + given_vector[:x],
y: curr_vector[:y] + given_vector[:y]
}
end
@doc """
Decrement one vector by another one
## Examples
iex> Vector2D.sub(%{x: 2, y: 2}, %{x: 2, y: 2})
%{x: 0, y: 0}
iex> Vector2D.new(-3, 3) |> Vector2D.sub(%{x: 3, y: 6})
%{x: -6, y: -3}
"""
@spec sub(%{x: number, y: number}, %{x: number, y: number}) :: %{x: number, y: number}
def sub(curr_vector, given_vector) do
%{
x: curr_vector[:x] - given_vector[:x],
y: curr_vector[:y] - given_vector[:y]
}
end
@doc """
Scale vector by given scalar
## Examples
iex> Vector2D.scale(%{x: 2, y: 2}, 2)
%{x: 4, y: 4}
iex> Vector2D.new(-3, 3) |> Vector2D.scale(3)
%{x: -9, y: 9}
iex> Vector2D.new(-3, 3.12) |> Vector2D.scale(3.001)
%{x: -9.003, y: 9.36312}
"""
@spec scale(%{x: number, y: number}, number) :: %{x: number, y: number}
def scale(vector, scalar) do
%{
x: vector[:x] * scalar,
y: vector[:y] * scalar
}
end
@doc """
Multiply vector by another one
## Examples
iex> Vector2D.multiply(%{x: 2, y: 2}, %{x: 2, y: 2})
%{x: 4, y: 4}
iex> Vector2D.new(-3, 3) |> Vector2D.multiply(%{x: 2, y: 3})
%{x: -6, y: 9}
"""
@spec multiply(%{x: number, y: number}, %{x: number, y: number}) :: %{x: number, y: number}
def multiply(curr_vector, given_vector) do
%{
x: curr_vector[:x] * given_vector[:x],
y: curr_vector[:y] * given_vector[:y]
}
end
@doc """
Divide vector by another one
## Examples
iex> Vector2D.divide(%{x: 2, y: 2}, %{x: 2, y: 2})
%{x: 1.0, y: 1.0}
iex> Vector2D.new(-3, 3) |> Vector2D.divide(%{x: 2, y: 3})
%{x: -1.5, y: 1.0}
"""
@spec divide(%{x: number, y: number}, %{x: number, y: number}) :: %{x: number, y: number}
def divide(curr_vector, given_vector) do
%{
x: curr_vector[:x] / given_vector[:x],
y: curr_vector[:y] / given_vector[:y]
}
end
@doc """
Check if given vectors are equal
## Examples
iex> Vector2D.equals(%{x: 2, y: 2}, %{x: 2, y: 2})
true
iex> Vector2D.equals(%{x: 2, y: 2}, %{x: 2.0, y: 2.0})
true
iex> Vector2D.equals(%{x: 2.0, y: 2.0}, %{x: 2, y: 2})
true
iex> Vector2D.equals(%{x: 2.0, y: 2.0}, %{x: 2, y: 2.0})
true
iex> Vector2D.new(-3, 3) |> Vector2D.equals(%{x: 2, y: 3})
false
"""
@spec equals(%{x: number, y: number}, %{x: number, y: number}) :: boolean
def equals(curr_vector, given_vector) do
curr_vector[:x] / 1 === given_vector[:x] / 1 &&
curr_vector[:y] / 1 === given_vector[:y] / 1
end
@doc """
Convert vector from struct to list
## Examples
iex> Vector2D.to_list(%{x: 1, y: 2})
[1, 2]
"""
@spec to_list(%{x: number, y: number}) :: [...]
def to_list(vector), do: [vector[:x], vector[:y]]
@doc """
Convert vector from list to struct
## Examples
iex> Vector2D.to_struct([1, 2])
%{x: 1, y: 2}
"""
@spec to_list([...]) :: %{x: number, y: number}
def to_struct(vector) do
%{
x: Enum.at(vector, 0),
y: Enum.at(vector, 1)
}
end
@doc """
Calculate distance between vectors
## Examples
iex> Vector2D.distance(%{x: 2.0, y: 2.0}, %{x: 2, y: 2.0})
0.0
iex> Vector2D.distance(%{x: -2, y: 4}, %{x: 2, y: 2.0})
4.47213595499958
"""
@spec distance(%{x: number, y: number}, %{x: number, y: number}) :: float
def distance(curr_vector, given_vector) do
delta_x = curr_vector[:x] - given_vector[:x]
delta_y = curr_vector[:y] - given_vector[:y]
:math.sqrt(:math.pow(delta_x, 2) + :math.pow(delta_y, 2))
end
@doc """
Rotate vector by angel
## Examples
iex> Vector2D.rotate(%{x: 2.0, y: 2.0}, 2)
%{x: -2.650888526745648, y: 0.9863011805570786}
"""
@spec rotate(%{x: number, y: number}, number) :: %{x: number, y: number}
def rotate(vector, angle) do
cos = :math.cos(angle)
sin = :math.sin(angle)
%{
x: vector[:x] * cos - vector[:y] * sin,
y: vector[:x] * sin + vector[:y] * cos
}
end
end