defmodule Graphmath.Vec3 do @moduledoc """ This is the 3D mathematics library for graphmath. This submodule handles 3D vectors using tuples of floats. """ @type vec3 :: {float, float, float} @doc""" `create()` creates a zeroed `vec3`. It takes no arguments. It returns a `vec3` of the form `{ 0.0, 0.0, 0.0 }`. """ @spec create() :: vec3 def create() do {0.0, 0.0, 0.0} end @doc""" `create(x,y,z)` creates a `vec3` of value (x,y,z). `x` is the first element of the `vec3` to be created. `y` is the second element of the `vec3` to be created. `z` is the third element of the `vec3` to be created. It returns a `vec3` of the form `{x,y,z}`. """ @spec create(float,float,float) :: vec3 def create( x, y, z) do {x,y,z} end @doc""" `create(vec)` creates a `vec3` from a list of 3 or more floats. `vec` is a list of 3 or more floats. It returns a `vec3` of the form `{x,y,z}`, where `x`, `y`, and `z` are the first three elements in `vec`. """ @spec create([float]) :: vec3 def create( vec ) do [x,y,z | _] = vec {x,y,z} end @doc """ `add( a, b)` adds two `vec3`s. `a` is the first `vec3`. `b` is the second `vec3`. It returns a `vec3` of the form { ax + bx, ay + by, az + bz }. """ @spec add( vec3, vec3 ) :: vec3 def add( a, b ) do { x, y, z } = a { u, v, w } = b { x+u, y+v, z+w } end @doc """ `subtract(a, b)` subtracts one `vec3` from another `vec3`. `a` is the `vec3` minuend. `b` is the `vec3` subtrahend. It returns a `vec3` of the form { ax - bx, ay - by, az - bz }. (the terminology was found [here](http://mathforum.org/library/drmath/view/58801.html)). """ @spec subtract( vec3, vec3 ) :: vec3 def subtract( a, b ) do { x, y, z } = a { u, v, w } = b { x-u, y-v, z-w } end @doc """ `multiply( a, b)` multiplies element-wise a `vec3` by another `vec3`. `a` is the `vec3` multiplicand. `b` is the `vec3` multiplier. It returns a `vec3` of the form { axbx, ayby, azbz }. """ @spec multiply( vec3, vec3 ) :: vec3 def multiply( a, b ) do { x, y, z } = a { u, v, w } = b { x*u, y*v, z*w } end @doc """ `scale( a, scale)` uniformly scales a `vec3`. `a` is the `vec3` to be scaled. `scale` is the float to scale each element of `a` by. It returns a tuple of the form { axscale, ayscale, azscale }. """ @spec scale( vec3, float ) :: vec3 def scale( a, scale ) do { x,y,z } = a { x*scale, y*scale, z*scale } end @doc """ `dot( a, b)` finds the dot (inner) product of one `vec3` with another `vec3`. `a` is the first `vec3`. `b` is the second `vec3`. It returns a float of the value (axbx + ayby + azbz). """ @spec dot( vec3, vec3 ) :: float def dot( a, b ) do { x, y, z } = a { u, v, w } = b (x*u)+(y*v)+(z*w) end @doc """ `cross( a, b)` finds the cross productof one `vec3` with another `vec3`. `a` is the first `vec3`. `b` is the second `vec3`. It returns a float of the value ( aybz - azby, azbx - axbz, axby - aybx). The cross product of two vectors is a vector perpendicular to the two source vectors. Its magnitude will be the area of the parallelogram made by the two souce vectors. """ @spec cross( vec3, vec3 ) :: vec3 def cross( a, b ) do { x, y, z } = a { u, v, w } = b { y*w - z*v, z*u - x*w, x*v - y*u } end @doc """ `length(a)` finds the length (Eucldiean or L2 norm) of a `vec3`. `a` is the `vec3` to find the length of. It returns a float of the value (sqrt( ax2 + ay2 + az2)). """ @spec length( vec3 ) :: float def length( a ) do { x, y, z } = a :math.sqrt( (x*x) + (y*y) + (z*z) ) end @doc """ `length_squared(a)` finds the square of the length of a `vec3`. `a` is the `vec3` to find the length squared of. It returns a float of the value ax2 + ay2 + az2. In many cases, this is sufficient for comparisons and avoids a square root. """ @spec length_squared( vec3 ) :: float def length_squared( a ) do { x, y, z } = a (x*x) + (y*y) + (z*z) end @doc """ `length_manhattan(a)` finds the Manhattan (L1 norm) length of a `vec3`. `a` is the `vec3` to find the Manhattan length of. It returns a float of the value (ax + ay + az). The Manhattan length is the sum of the components. """ @spec length_manhattan( vec3 ) :: float def length_manhattan( a ) do { x, y, z } = a x + y + z end @doc """ `normalize(a)` finds the unit vector with the same direction as a `vec3`. `a` is the `vec3` to be normalized. It returns a `vec3` of the form `{normx, normy, normz}`. This is done by dividing each component by the vector's magnitude. """ @spec normalize( vec3 ) :: vec3 def normalize( a ) do { x, y, z } = a imag = 1 / :math.sqrt( (x*x) + (y*y) + (z*z) ) {x * imag, y * imag, z * imag} end @doc """ `lerp(a,b,t)` linearly interpolates between one `vec3` and another `vec3` along an interpolant. `a` is the starting `vec3`. `b` is the ending `vec3`. `t` is the interpolant float, on the domain [0,1]. It returns a `vec3` 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( vec3, vec3, float) :: vec3 def lerp( a, b, t ) do { x, y, z } = a { u, v, w } = b { ( t * u) + ( (1-t) *x ), (t * v) + ( (1-t) *y), (t * w) + ( (1-t) * z)} end @doc """ `near(a,b, distance)` checks whether two `vec3`s are within a certain distance of each other. `a` is the first `vec3`. `b` is the second `vec3`. `distance` is the distance between them as a float. """ @spec near( vec3, vec3, float) :: boolean def near( a, b, distance) do { x, y, z } = a { u, v, w } = b dx = u - x dy = v - y dz = w - z distance > :math.sqrt( dx*dx + dy*dy + dz*dz) end @doc """ `rotate( v, k, theta)` rotates a vector (v) about a unit vector (k) by theta radians. `v` is the `vec3` to be rotated. `k` is the `vec3` axis of rotation. *It must be of unit length*. `theta` is the angle in radians to rotate as a float. This uses the [Formula of Rodriguez](http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula): **V**rot = **V**cos(theta) + (**K** x **V**)sin(theta) + **K**(**K** dot **V**)(1-cos(theta)) """ @spec rotate( vec3, vec3, float) :: vec3 def rotate( v, k, theta) do { vx, vy, vz } = v { kx, ky, kz } = k ct = :math.cos(theta) st = :math.sin(theta) k_dot_v = ( (vx*kx) + (vy*ky) + (vz*kz) ) coeff = (1.0-ct) * k_dot_v scale( v, ct) |> add( scale( cross( k, v), st) ) |> add( scale(k, coeff) ) end end