Vectors
vec2, vec3 and vec4 are vectors of 2, 3 and 4 single floats. Every
function below exists for each size, named with its prefix, vec2-,
vec3- or vec4-, except vec3-cross, and for doubles, as dvec3- and so
on; see Doubles. Functions that return a vector take an optional
last argument, result; see Results and Allocation.
Making vectors
(gm:make-vec3 :x 1 :y 2 :z 3) ; => #(1.0 2.0 3.0)
(gm:make-vec3 :y 1) ; => #(0.0 1.0 0.0)
(gm:make-vec3) ; => #(0.0 0.0 0.0)
(gm:make-vec4 :x 1/2) ; => #(0.5 0.0 0.0 0.0)
make-vec3 takes the components by keyword, as any reals, and those not
given are 0, W included. vec3-copy copies a vector, and
vec3-set sets all of one's components and returns it:
(gm:vec3-set (gm:make-vec3) 0 0 1) ; => #(0.0 0.0 1.0)
Components
A vector is also a typed structure over its array, as
(defstruct (vec3 (:type (vector single-float))) x y z) makes one, so each
component has an accessor named for the type: vec2-x and vec2-y,
vec3-x to vec3-z, and vec4-x to vec4-w. setf writes a component:
(let ((v (gm:make-vec3 :x 1 :y 2 :z 3)))
(setf (gm:vec3-x v) 10.0)
v)
;; => #(10.0 2.0 3.0)
A component takes a float of its vector's precision and nothing else, as
its array holds: (setf (gm:vec3-x v) 10) signals type-error, as
(setf (aref v 0) 10) does, and SBCL warns of it when it compiles a
constant. The constructors and vec3-set take any reals.
An accessor checks for its own type, so vec3-x refuses a vec4, and
vec3-z a vec2, with type-error.
Arithmetic
| Function | Returns |
|---|---|
vec3+, vec3- |
The sum and the difference. |
vec3*, vec3/ |
The product and the quotient, component by component. |
vec3-scale |
The vector times a real. |
vec3-negate |
The vector pointing the other way. |
vec3-min, vec3-max |
The lesser and the greater component of each pair. |
vec3-lerp |
The point a fraction of the way from one vector to another: the first at 0, the second at 1. |
vec3-cross |
The cross product, perpendicular to both, by the right-hand rule. vec3 only. |
(gm:vec3-scale (gm:make-vec3 :x 1 :y 2 :z 3) 2) ; => #(2.0 4.0 6.0)
(gm:vec3* (gm:make-vec3 :x 1 :y 2 :z 3) (gm:make-vec3 :x 2 :y 2 :z 2)) ; => #(2.0 4.0 6.0)
(gm:vec3-min (gm:make-vec3 :x 1 :y 5 :z 3) (gm:make-vec3 :x 4 :y 2 :z 6)) ; => #(1.0 2.0 3.0)
(gm:vec3-lerp (gm:make-vec3) (gm:make-vec3 :x 10 :y 20 :z 30) 1/4) ; => #(2.5 5.0 7.5)
(gm:vec3-cross (gm:make-vec3 :x 1) (gm:make-vec3 :y 1)) ; => #(0.0 0.0 1.0)
Lengths, distances and angles
| Function | Returns |
|---|---|
vec3-dot |
The dot product, a single float. |
vec3-length |
The length. |
vec3-length-squared |
The length squared: cheaper, and enough to compare lengths. |
vec3-distance |
The distance between two points. |
vec3-distance-squared |
The distance squared: cheaper, and enough to compare distances. |
vec3-angle |
The angle between two vectors, in radians, from 0 to π; 0 if either is zero. |
vec3-normalize |
The vector scaled to length 1. A zero vector stays zero. |
(gm:vec3-dot (gm:make-vec3 :x 1 :y 2 :z 3) (gm:make-vec3 :x 4 :y 5 :z 6)) ; => 32.0
(gm:vec3-angle (gm:make-vec3 :x 1) (gm:make-vec3 :y 1)) ; => 1.5707964
(gm:vec3-normalize (gm:make-vec3 :x 3 :z 4)) ; => #(0.6 0.0 0.8)
(gm:vec3-normalize (gm:make-vec3)) ; => #(0.0 0.0 0.0)
(let ((a (gm:make-vec3 :x 1 :y 1 :z 1))
(b (gm:make-vec3 :x 4 :y 5 :z 1)))
(list (gm:vec3-distance a b) (gm:vec3-distance-squared a b)))
;; => (5.0 25.0)
vec3-angle uses Kahan's formula, which keeps its precision for vectors
nearly parallel or nearly opposite, where the arc cosine of the normalised
dot product loses it.
Normalising never divides by zero: a zero vector stays zero, as in Godot, so a direction that may vanish, such as a character's input, needs no check first.
Reflecting and projecting
| Function | Returns |
|---|---|
(vec3-reflect incident normal) |
incident reflected off a surface whose unit normal is normal, as GLSL's reflect does it. |
(vec3-project vector onto) |
The part of vector along onto, which need not be unit. Onto a zero vector, zero. |
(vec3-project-on-plane vector normal) |
vector less its part along normal, which need not be unit: what is left in the plane, as a body sliding along a wall moves. A zero normal leaves vector as it is. |
(gm:vec3-reflect (gm:make-vec3 :x 1 :y -1) (gm:make-vec3 :y 1)) ; => #(1.0 1.0 0.0)
(gm:vec3-project (gm:make-vec3 :x 3 :y 4) (gm:make-vec3 :x 2)) ; => #(3.0 0.0 0.0)
(gm:vec3-project-on-plane (gm:make-vec3 :x 3 :y -4) (gm:make-vec3 :y 1)) ; => #(3.0 0.0 0.0)
A ball moving down and to the right bounces up off a floor, and a body pushed down and to the right slides along it to the right.
Limiting and moving
| Function | Returns |
|---|---|
(vec3-clamp-length vector max-length) |
vector shortened to max-length if it is longer, and otherwise as it is: a speed limit. max-length is a real of 0 or more. |
(vec3-move-toward from to delta) |
The point delta further from from toward to, but no further than to, as Godot's move_toward: moving toward a target at a speed. A negative delta moves away. |
(gm:vec3-clamp-length (gm:make-vec3 :x 3 :y 4) 2) ; => #(1.2 1.6 0.0)
(gm:vec3-move-toward (gm:make-vec3) (gm:make-vec3 :x 10) 3) ; => #(3.0 0.0 0.0)
(gm:vec3-move-toward (gm:make-vec3 :x 9) (gm:make-vec3 :x 10) 3) ; => #(10.0 0.0 0.0)
The same for single numbers is on Single Numbers.
Comparing
Floats that went through arithmetic rarely compare equal.
vec3-approx= is true if no component differs by more than an epsilon,
1e-5 by default:
(gm:vec3-approx= (gm:make-vec3 :x 1 :y 2 :z 3) (gm:make-vec3 :x 1 :y 2 :z 3.000001)) ; => T