This library implements types, operators, and algorithms commonly used in math for 2D and 3D graphics.
It supersedes and combines the prior libraries 3d-vectors, 3d-matrices, 3d-quaternions, and 3d-transfroms. The new API is largely but not entirely backwards compatible, and adds a lot of new functionality.
The API is split over five packages for each data type:
org.shirakumo.fraf.math.vectors
org.shirakumo.fraf.math.matrices
org.shirakumo.fraf.math.quaternions
org.shirakumo.fraf.math.dual-quaternions
org.shirakumo.fraf.math.transforms
With the org.shirakumo.fraf.math
package combining them all into one. All packages are designed to be :use
d, and follow strict function name prefixing to avoid name clashes. The following naming rules apply:
!
n
v
for vectorsm
for matricesq
for quaternionsq2
for dual-quaternionst
for transformsSome example operations would be: v+
, nm*
, !qinv
For the data types themselves similar rules apply:
single-float
d
for double-float
i
for (signed-byte 32)
u
for (unsigned-byte 32)
*
for "any of the above"vec
for vectorsmat
for matricesquat
for quaternionsquat2
for dual-quaternionstransform
for transforms2
for a 2-dimensional vector or 2x2 square matrix3
for a 3-dimensional vector or 3x3 square matrix4
for a 4-dimensional vector or 4x4 square matrixn
for a nxm arbitrary matrix*
for "any of the above"Some example types would be: vec2
, dmatn
, quat
, vec*
*mat3
The following feature flags indicate which types are supported:
:3d-math-f32
the single-float
types:3d-math-f64
the double-float
types:3d-math-i32
the (signed-byte 32)
types:3d-math-u32
the (unsigned-byte 32)
typesIf you would like to disable a specific type, push the corresponding :3d-math-no-*
feature before compiling the library. By default (unsigned-byte 32)
types are not available. In order to get them, you must similarly push :3d-math-u32
before compiling.
The vectors API supports the following operations:
vec
, vec2
, vec3
, vec4
dvec
, dvec2
, dvec3
, dvec4
uvec
, uvec2
, uvec3
, uvec4
ivec
, ivec2
, ivec3
, ivec4
vrand
, vzero
, vcopy
vx
, vy
, vz
, vw
, varr
v=
, v~=
, v/=
, v<
, v<=
, v>
, v>=
v+
, v-
, v*
, v/
, v+*
, vincf
, vdecf
, v1+
, v1-
, vscale
, vc
, v.
vmin
, vmax
, vabs
, vmod
, vfloor
, vceiling
, vround
, valign
v1norm
, vinorm
, v2norm
, vpnorm
vdistance
, vsqrdistance
, vlength
, vsqrlength
, vangle
vrot
, vrot2
, vrotv
vpolar
, vcartesian
vlerp
vinv
, vunit
, vunit*
+vx2+
, +vy2+
, +vx3+
, +vy3+
, +vz3+
, +vx4+
, +vy4+
, +vz4+
, +vw4+
-vx2+
, -vy2+
, -vx3+
, -vy3+
, -vz3+
, -vx4+
, -vy4+
, -vz4+
, -vw4+
v<-
, vsetf
, with-vec
The matrices API supports the following operations:
mat
, mat2
, mat3
, mat4
, matn
dmat
, dmat2
, dmat3
, dmat4
, dmatn
umat
, umat2
, umat3
, umat4
, umatn
imat
, imat2
, imat3
, imat4
, imatn
mcopy
, mzero
, mrand
, meye
, mblock
marr
, mcref
, miref
m=
, m~=
, m/=
, m<
, m<=
, m>
, m>=
m+
, m-
, m*
, m/
mmin
, mmax
m1norm
, minorm
, m2norm
nmtranslate
, nmscale
, nmrotate
mtranslation
, mscaling
, mrotation
mlookat
, mfrustum
, mperspective
, mortho
mrow
, mcol
, mdiag
, mminor
, mdet
, mtrace
mpivot
, mlu
, mqr
, meigen
, mcofactor
, mcof
, madj
minv
, minv-affine
, mtranspose
mswap-row
, mswap-col
, mapply
, mtransfer
m<-
, msetf
, with-fast-matref
Quaternions represent a rotation or orientation in 3D space. The advantage over the matrix representation is that quaternions are not subject to gimbal lock and offer faster and more precise manipulation over time.
The quaternions API supports the following operations:
quat
, dquat
qfrom-angle
, qtowards
, qlookat
qrand
, qcopy
, qzero
qmat
, qfrom-mat
qx
, qy
, qz
, qw
qi
, qj
, qk
, qr
qaxis
, qangle
q=
, q~=
, q/=
, q<
, q<=
, q>
, q>=
q+
, q-
, q*
, q/
, q+*
, qexpt
, q.
qlength
, qsqrlength
qmin
, qmax
qmix
, qnlerp
, qslerp
qinv
, qunit
, qunit*
, qconjugate
q<-
, qsetf
Dual quaternions represent a rotation and a translation, without scaling. They can be useful as an alternative representation of transformations, as they preserve twists without loss, while linear matrix interpolation leads to a "squeezing" effect.
The dual-quaternions API supports the following operations:
quat2
, dquat2
q2location
, q2from-location
q2copy
, q2zero
q2real
, q2dual
q2=
, q2~=
, q2/=
q2+
, q2-
, q2*
, q2/
, q2.
q2length
, q2sqrlength
q2unit
, q2unit*
, q2conjugate
q2<-
Transforms encapsulate a translation, rotation, and scaling, sometimes also referred to as a "gizmo". It allows encapsulating the transformation of an object without falling victim to gimbal lock or drifting precision from the transform matrix representation.
The transforms API supports the following operations:
transform
, dtransform
tmat
, tfrom-mat
tquat2
, tfrom-quat2
tcopy
, tzero
tlocation
, tscaling
, trotation
t=
, t~=
, t/=
t+
, t-
, t*v
, t*p
, t*p-inv
tmix
tinv
tmove
, toffset
, tscale
, trotate
tmove-by
, toffset-by
, tscale-by
, trotate-by
t<-
, tx
, ty
, tz
Each data type is split off into its own module as follows:
package.lisp
Additional exports not covered by auto-exportingtypes.lisp
Definition of the basic template data types, accessors, and constructorsraw-ops.lisp
Definition of the raw type-specific operation functionsops.lisp
Definition of the user-facing dispatching operationstest.lisp
Unit testsHeavy use is made of the "type-templates"(https://shinmera.github.io/type-templates) system to allow generating the many, many different variants of operations for each type combination, as well as the complex dispatching behaviour.
The core template types are vec-type
, mat-type
, quat-type
, quat2-type
, and transform-type
respectively. We also provide and heavily make use of the custom types f32
, f64
, u32
and i32
to define the contained immediate value types.
The shared functions and abbreviating macros used for quickly defining the common reductors, type dispatchers, etc. can be found in toolkit.lisp
.
Given a suitably good compiler, such as recent SBCL versions, a lot of the 3d-math results can be stack-allocated to avoid triggering GC by declaring them dynamic-extent
. Even relatively complex forms can be automatically transformed to stack allocations. To check whether this is successful, you can use sb-ext:stack-allocated-p
on the value. In cases where the automatic inference fails or you need more precise control, you should be able to use the plain value constructors such as vec2
, declare the variable the result is bound to to be dynamic-extent
, and then proceed to use the destructive variants of the various operators such as nv+
or !v+
rather than v+
.