Table of Contents
2D Transforms
A mat3x2 is a 2D affine transform: a matrix of 3 columns and 2 rows, as
GLSL names it, holding the X axis, the Y axis and the origin. Its six
elements, in order, are the a b c d e f of a canvas transform: a point x, y
goes to a x + c y + e, b x + d y + f. Functions that return a matrix or a
vector take an optional last argument, result; see
Results and Allocation. dmat3x2 is the same of
doubles; see Doubles.
A node's transform
mat3x2-compose makes a 2D node's transform from a translation, an angle
and a scale: scaled, then turned, then moved. mat3x2* puts a parent's
transform before a child's, and mat3x2-inverse takes points back:
(let* ((parent (gm:mat3x2-compose (gm:make-vec2 :x 100 :y 50) 0 (gm:make-vec2 :x 2 :y 2)))
(child (gm:mat3x2-compose (gm:make-vec2 :x 10) (/ pi 2) (gm:make-vec2 :x 1 :y 1)))
(global (gm:mat3x2* parent child)))
(values (gm:mat3x2-transform-point global (gm:make-vec2 :x 1))
(gm:mat3x2-transform-point (gm:mat3x2-inverse global)
(gm:make-vec2 :x 120 :y 52))))
;; => #(120.0 52.0), #(1.0 0.0)
The child turns its point (1, 0) a quarter turn to (0, 1) and moves it to (10, 1); the parent scales that to (20, 2) and moves it to (120, 52).
A positive angle turns +X toward +Y: counterclockwise where Y points up, and clockwise on a screen, where it points down. Whether +Y is up or down is the renderer's choice; the matrices only turn +X toward +Y.
Making them
| Function | Returns the mat3x2 that |
|---|---|
(make-mat3x2 a b c d e f) |
holds the six elements, column by column. Another number of them signals gmath-error. |
(mat3x2-identity) |
leaves points where they are. |
(mat3x2-translation offset) |
moves points by the vec2 offset. |
(mat3x2-rotation angle) |
turns points by angle, in radians, from +X toward +Y. |
(mat3x2-scaling scale) |
scales points by the vec2 scale, along each axis. |
(mat3x2-compose translation angle scale) |
scales, then turns, then moves: what (mat3x2* T (mat3x2* R S)) makes, in one step. |
(gm:mat3x2-translation (gm:make-vec2 :x 5 :y 7)) ; => #(1.0 0.0 0.0 1.0 5.0 7.0)
Operations
| Function | Returns |
|---|---|
(mat3x2* a b) |
The product A B: the transform that applies B, then A. |
(mat3x2-transform-point matrix point) |
The vec2 point turned, scaled and moved. |
(mat3x2-transform-direction matrix direction) |
The vec2 direction turned and scaled, not moved. |
(mat3x2-inverse matrix) |
The inverse, which takes points back. Signals gmath-error for a singular matrix, which squashes the plane onto a line or a point. |
(mat3x2-determinant matrix) |
The factor by which it scales areas, negative if it mirrors them. |
(mat3x2-copy matrix) |
A copy. |
(mat3x2-approx= a b) |
True if no element differs by more than an epsilon, 1e-5 by default. |
(gm:mat3x2-determinant (gm:mat3x2-scaling (gm:make-vec2 :x 2 :y 3))) ; => 6.0
(gm:mref (gm:make-mat3x2 1 2 3 4 5 6) 1 2) ; => 6.0, the origin's Y
mref reads an element by its row, 0 or 1, and its column, 0 to 2, and
setf writes it.
As a 3x3
A mat3x2 is the mat3 of homogeneous 2D points without its bottom row,
which is always 0 0 1. mat3-from-mat3x2 adds the row, for code that wants
a mat3, and mat3x2-from-mat3 drops it:
(gm:mat3-from-mat3x2 (gm:mat3x2-translation (gm:make-vec2 :x 5 :y 7)))
;; => #(1.0 0.0 0.0 0.0 1.0 0.0 5.0 7.0 1.0)
The transforms have functions of their own as mat3s of homogeneous
points too, which cl-gsl's
cl-gsl/gmath also makes shader code:
| Function | Returns |
|---|---|
(mat3-translation-2d offset) |
The mat3 that moves 2D points by the vec2 offset. |
(mat3-rotation-2d angle) |
The mat3 that rotates 2D points by angle, from +X toward +Y. |
(mat3-scaling-2d scale) |
The mat3 that scales 2D points by the vec2 scale. |
(mat3-transform-point-2d matrix point) |
The vec2 point rotated, scaled and translated. |
(mat3-transform-direction-2d matrix direction) |
The vec2 direction rotated and scaled, without the translation. |
A mat3x2 computes exactly what its mat3 computes, bit for bit, so the
two give the same results:
(let ((m (gm:mat3x2* (gm:mat3x2-translation (gm:make-vec2 :x 10))
(gm:mat3x2-rotation (gm:radians 90)))))
(list (gm:mat3x2-transform-point m (gm:make-vec2 :x 1))
(gm:mat3x2-transform-direction m (gm:make-vec2 :x 1))))
;; => (#(10.0 1.0) #(-4.371139e-8 1.0))
(let ((m (gm:mat3* (gm:mat3-translation-2d (gm:make-vec2 :x 10))
(gm:mat3-rotation-2d (gm:radians 90)))))
(list (gm:mat3-transform-point-2d m (gm:make-vec2 :x 1))
(gm:mat3-transform-direction-2d m (gm:make-vec2 :x 1))))
;; => (#(10.0 1.0) #(-4.371139e-8 1.0))
The tiny X, rather than 0, is rounding: single floats hold π/2 only approximately.