defmodule Graphmath.Vec2 do @moduledoc """ This is the 2D mathematics library for graphmath. This submodule handles vectors stored as a tuple. """ @type vec2 :: { float, float } @doc""" `create()` creates a zero vec2. It will return a tuple of the form {0.0,0.0}. `create()` creates a zeroed `vec2`. It takes no arguments. It returns a `vec2` of the form `{ 0.0, 0.0 }`. """ @spec create() :: vec2 def create() do {0.0,0.0} end @doc""" `create(x,y)` creates a `vec2` of value (x,y). `x` is the first element of the `vec3` to be created. `y` is the second element of the `vec3` to be created. It returns a `vec2` of the form `{x,y}`. """ @spec create(float,float) :: vec2 def create(x,y) do {x,y} end @doc""" `create(vec)` creates a `vec2` from a list of 2 or more floats. `vec` is a list of 2 or more floats. It returns a `vec2` of the form `{x,y}`, where `x` and `y` are the first three elements in `vec`. """ @spec create([float]) :: vec2 def create( vec ) do [x,y | _] = vec {x,y} end @doc """ `add( a, b)` adds a vec2 (a) to a vec2 (b). It returns a tuple of the form { ax + bx, ay + by }. `add( a, b )` adds two `vec2`s. `a` is the first `vec2`. `b` is the second `vec2`. It returns a `vec2` of the form { ax + bx, ay + by }. """ @spec add( vec2, vec2 ) :: vec2 def add( a, b ) do { x,y } = a { u,v } = b { x+u, y+v } end @doc """ `subtract(a, b )` subtracts one `vec2` from another `vec2`. `a` is the `vec2` minuend. `b` is the `vec2` subtrahend. It returns a `vec2` of the form { ax - bx, ay - by }. (the terminology was found [here](http://mathforum.org/library/drmath/view/58801.html)). """ @spec subtract( vec2, vec2 ) :: vec2 def subtract( a, b ) do { x,y } = a { u,v } = b {x-u,y-v} end @doc """ `multiply( a, b)` mulitplies element-wise a vec2 (a) by a vec2 (b). It returns a tuple of the form { ax*bx, ay*by }. `multiply( a, b )` multiplies element-wise a `vec2` by another `vec2`. `a` is the `vec2` multiplicand. `b` is the `vec2` multiplier. It returns a `vec2` of the form { axbx, ayby }. """ @spec multiply( vec2, vec2 ) :: vec2 def multiply( a, b ) do { x,y } = a { u,v } = b { x*u, y*v } end @doc """ `scale( a, scale )` uniformly scales a `vec2`. `a` is the `vec2` to be scaled. `scale` is the float to scale each element of `a` by. It returns a tuple of the form { axscale, ayscale }. """ @spec scale( vec2, float ) :: vec2 def scale( a, scale ) do { x,y } = a { x*scale, y*scale } end @doc """ `dot( a, b )` finds the dot (inner) product of one `vec2` with another `vec2`. `a` is the first `vec2`. `b` is the second `vec2`. It returns a float of the value (axbx + ayby ). """ @spec dot( vec2, vec2 ) :: float def dot( a, b) do { x,y } = a { u,v } = b (x*u)+(y*v) end @doc """ `perp_prod( a, b )` finds the perpindicular product of one `vec2` with another `vec2`. `a` is the first `vec2`. `b` is the second `vec2`. The perpindicular product is the magnitude of the cross-product between the two vectors. It returns a float of the value (axby - bxay). """ @spec perp_prod( vec2, vec2 ) :: float def perp_prod( a, b ) do { x,y } = a { u,v } = b (x*v) - (u*y) end @doc """ `length(a)` finds the length (Eucldiean or L2 norm) of a `vec2`. `a` is the `vec2` to find the length of. It returns a float of the value (sqrt( ax2 + ay2)). """ @spec length( vec2 ) :: float def length( a ) do { x,y } = a :math.sqrt( (x*x) + (y*y) ) end @doc """ `length_squared(a)` finds the square of the length of a vec2 (a). In many cases, this is sufficient for comparisions and avaoids a sqrt. It returns a float of the value (ax*ax + ay*ay). `length_squared(a)` finds the square of the length of a `vec2`. `a` is the `vec2` to find the length squared of. It returns a float of the value ax2 + ay2. In many cases, this is sufficient for comparisons and avoids a square root. """ @spec length_squared( vec2 ) :: float def length_squared( a ) do { x,y } = a (x*x) + (y*y) end @doc """ `length_manhattan(a)` finds the Manhattan (L1 norm) length of a `vec2`. `a` is the `vec2` to find the Manhattan length of. It returns a float of the value (ax + ay). The Manhattan length is the sum of the components. """ @spec length_manhattan( vec2 ) :: float def length_manhattan( a ) do { x,y } = a x + y end @doc """ `normalize(a)` finds the unit vector with the same direction as a `vec2`. `a` is the `vec2` to be normalized. It returns a `vec2` of the form `{normx, normy}`. This is done by dividing each component by the vector's magnitude. """ @spec normalize( vec2 ) :: vec2 def normalize( a ) do { x,y } = a invmag = 1 / :math.sqrt( (x*x) + (y*y) ) {x * invmag, y * invmag} end @doc """ `lerp(a,b,t)` is used to linearly interpolate between two given vectors a and b along an interpolant t. The interpolant `t` is on the domain [0,1]. Behavior outside of that is undefined. `lerp(a,b,t)` linearly interpolates between one `vec2` and another `vec2` along an interpolant. `a` is the starting `vec2`. `b` is the ending `vec2`. `t` is the interpolant float, on the domain [0,1]. It returns a `vec2` of the form (1-t)**a** - (t)**b**. The interpolant `t` is on the domain [0,1]. Behavior outside of that is undefined. """ @spec lerp( vec2, vec2, float) :: vec2 def lerp( a, b, t ) do { x,y } = a { u,v } = b { (t*u) + ((1-t)*x), (t*v) + ((1-t)*y) } end @doc """ `rotate(a,theta)` rotates a `vec2` CCW about the +Z axis. `a` is the `vec2` to rotate. `theta` is the number of radians to rotate by as a float. This returns a `vec2`. """ @spec rotate( vec2, float) :: vec2 def rotate( a, theta) do { x,y } = a ct = :math.cos(theta) st = :math.sin(theta) { x*ct + y*st, x*st - y*ct } end @doc """ `near(a,b, distance)` checks whether two `vec2`s are within a certain distance of each other. `a` is the first `vec2`. `b` is the second `vec2`. `distance` is the distance between them as a float. """ @spec near( vec2, vec2, float) :: boolean def near( a, b, distance) do { x,y } = a { u,v } = b dx = x-u dy = y-v distance > :math.sqrt( dx*dx + dy*dy ) end @doc """ `project(a,b)` projects one `vec2` onto another `vec2`. `a` is the first `vec2`. `b` is the second `vec2`. This returns a `vec2` representing the image of `a` in the direction of `b`. """ @spec project( vec2, vec2 ) :: vec2 def project( a,b ) do { x,y } = a { u,v } = b coeff = ((x*u) +(y*v)) / (u*u + v*v) {u*coeff, v*coeff} end @doc """ `perp(a)` creates a vector perpendicular to another vector `a`. `a` is the `vec2` to be perpindicular to. This returns a `vec2` perpindicular to `a`, to the right of the original `a`. """ @spec perp( vec2 ) :: vec2 def perp(a) do { x, y } = a { -y, x } end end