1 # cl-tuples - A set of macros for auto-generating optimised vector math routines
3 ## A tuple type declaration auto-generates a number of useful functions, macros, and types.
5 It is best to give an example.
7 (def-tuple-type vector2d
8 :tuple-element-type short-float
12 Will declare a tuple of short-floats, arrays of which are initialised
13 with the element 0.0f0 and which has two elements, named x and y.
15 There will be a struct to represent this type, declared as follows:
20 (x 0.0f0 :type short-float)
21 (y 0.0f0 :type short-float))
23 i.e. a struct, stored as a vector with elements representing the
24 elements of the tuple, initialised to the initial-element value of the
27 Literals can be written via the modified read syntax
29 #[ vector2d 0.2 1.2 ] => #( 0.2 1.2 )
30 #[ vector2d* 0.2 1.2 ] => (values 0.2 1.2)
32 It is reccomended literals are written with the above syntax as their
33 expansion will also incorportate type definitions that will be
34 compatible with the following routines that will be generated to be
35 able to manipulate them.
37 (vector2d-values* x y) => (values x y) ;; convert from args to values
38 (vector2d* v) => (values (aref v 0) (aref v 1)) ;; covert from array to values
39 (new-vector2d) ;; returns an empty tuple vector- i.e. #( 0 0 )
40 (make-vector2d x y) ;; returns a vector (struct) as #( x y )
41 (make-vector2d* (values x y)) ;; same as the above only with multiple value arguments
42 (setf (vector2d* v) (values x y) ) ;; generalised set that takes multiple values
43 (with-vector2d v (i j) ...) ;; binds x and y of tuple vector v to i and j in the body
44 (with-vector2d* (values x y) (i j) ..) ;; same as the above, only it expects a values form
46 ;; arrays -- this can create an array of n vector2ds (eg 4 vector2ds == 8 element array)
47 (make-vector2d-array dimensons &key adjustable fill-pointer)
49 (vector2d-aref v n) ;; treats v as an array of n vector2d's and
50 ;; returns the nth vector2d as a vector (ie
52 (vector2d-aref* v n) ;; treats v as an array of n vector2d's and returns the
53 ;; nth vector2 as multiple values
55 (setf (vector2d-aref v n) #( x y )) ;; sets the n'tn vector2d in the array v
57 (setf (vector2d-aref v n) (values x y )) ;; sets the n'tn vector2d in the array v, expects multiple
60 (vector2d-push #( x y ) v) ;; push an vector2d into an array of vector2d
61 (vector2d-push* (values x y) v) ;; same as above but with multiple values
62 (vector2d-push-extend #( x y ) v) ;; as vector2d-push but admits the possiblity of extension
63 (vector2d-push-extend* (values x y) v) ;; same as above but takes multiple value arguments
65 (vector2d-fill-pointer v) ;; returns fill pointer
66 (setf (vector2d-fill-pointer v) x) ;; sets fill pointer
67 (vector2d-array-dimensions v) ;; returns number of vector2d's array can hold
69 In addition a small convienince reader syntax is implemented - #{ x y
70 z } is equivalent to (values x y z) as client code of this library is
71 likely to manipulate many multiple values.
73 Note that the code cl-tuples generates is implementation agnostic: it
74 is heavily predicated on the assumption that your implementation does
75 a good job of optimising multiple value calls. If this is not the
76 case, then the convienence of the array - related functions are
77 probably the only good reason to use this library.
81 A two-dimensional vector value is created by `MAKE-VECTOR2D`:
83 > (make-vector2d 1f0 1f0)
86 The type `FAST-FLOAT`, which is used for all float values, is actually a
87 subtype of `SINGLE-FLOAT`, so make sure to only use values that fit into
90 To calculate the length of this vector `VECTOR2D-LENGTH*` can now be
93 > (let ((v (make-vector2d 1f0 1f0)))
94 (vector2d-length* (vector2d* v)))
97 By converting the object into a bunch of variables, the macro pipeline
98 keeps transient objects and function calls away. The above form thus
99 expands to something like the following (type declarations and some
100 other code omitted for clarity):
102 (LET ((V (MAKE-VECTOR2D 1.0 1.0)))
103 (MULTIPLE-VALUE-BIND (#:G1764 #:G1765)
104 (VALUES (AREF V 0) (AREF V 1))
105 (SYMBOL-MACROLET ((X #:G1764) (Y #:G1765))
106 (SQRT (+ (* X X) (* Y Y))))))
108 The coordinates of the vector are bound and made available to the length
109 calculation code. If we skip the object creation and go straight the
110 `VALUES` route, the following is approximately the same as above,
111 without ever creating a vector object.
113 > (vector2d-length* (vector2d-values 1.0 1.0))
116 The reader syntax may be used to the same effect:
118 > (enable-tuples-syntax)
122 > (vector2d-length* #{1.0 1.0})
125 (Since the reader syntax and `VECTOR2D-VALUES` expand directly into a
126 `VALUES` call, nothing prevents you from using that as well.)
128 Based on this design more operations are implemented. See the API and
129 the tests for details on vectors, vertexes, matrixes and quaternions.
131 Defining new operators is done via `DEF-TUPLE-OP`, e.g.:
133 (def-tuple-op scaling-matrix44*
142 0.0f0 0.0f0 0.0f0 1.0f0)))
144 This operator accepts three arguments and creates the obvious matrix
145 from them. So lets say, a function has as a conventional argument a
146 vector of three elements. Binding each element to a name and applying
147 the above operator to them gives us the following:
149 > (let ((v (make-vector3d* #{1f0 1f0 1f0))))
150 (with-vector3d v (sx sy sz)
151 (make-matrix44* (scaling-matrix44* sx sy sz)))
152 #(1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0)
154 The calculated matrix is converted to an actual object to be returned.
158 > (let ((v (make-vector2d 1f0 1f0))
160 (vector2d-length* (vector2d-scale* (vector2d* v) s)))
165 (Adapted from the documentation of cl-quaternion to this API.)
167 Creating a quaternion from real and imaginary components. The first
168 argument is the real part, and the rest are the imaginary components.
170 > (make-quaternion* (quaternion-values* 10f0 3f0 0f0 0f0))
173 Quaternions can be normalized and magnitudes may be computed.
175 > (make-quaternion* (quaternion-normalize* (quaternion* *)))
176 #(0.9578263 0.28734788 0.0 0.0)
177 > (quaternion-mag* (quaternion* *))
180 Quaternion addition and multiplication are supported.
183 (quaternion-sum* (quaternion-values* 3f0 0f0 0f0 0f0)
184 (quaternion-values* 1f0 1f0 0f0 1f0)))
187 (quaternion-product* (quaternion-values* 3f0 0f0 0f0 0f0)
188 (quaternion-values* 1f0 1f0 0f0 1f0)))
191 Unit quaternions may be used to represent rotations. Functions are
192 provided for working with quaternions for this purpose.
194 > (values fast-pi (type-of fast-pi))
198 (angle-axis-quaternion*
199 (angle-axis-values* 0f0 0f0 1f0 (/ single-pi 2f0))))
200 #(0.0 0.0 0.70710677 0.70710677)
202 Vectors can then be transformed using these quaternions.
204 > (quaternion-transform-vector3d*
205 (vector3d-values* 0.0 1.0 0.0)
206 (angle-axis-quaternion*
207 (angle-axis-values* 0.0 0.0 1.0 (/ fast-pi 2))))
212 At the moment you have still to convert an angle-axis representation to
213 either a matrix or a quaternion by yourself to rotate a vector by it.
215 > (quaternion-transform-vector3d*
216 (vector3d-values* 0.0 1.0 0.0)
217 (angle-axis-quaternion*
218 (angle-axis-values* 0.0 0.0 1.0 fast-pi)))
222 > (transform-vector3d*
223 (angle-axis-matrix33*
224 (angle-axis-values* 0.0 0.0 1.0 fast-pi))
225 (vector3d-values* 0.0 1.0 0.0))