summaryrefslogtreecommitdiff
path: root/chickadee/math/quaternion.scm
blob: 1f7c27bb14c2c2750326be0d0bc038459bd02bf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
;;; Chickadee Game Toolkit
;;; Copyright © 2017 David Thompson <dthompson2@worcester.edu>
;;;
;;; Chickadee is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published
;;; by the Free Software Foundation, either version 3 of the License,
;;; or (at your option) any later version.
;;;
;;; Chickadee is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see
;;; <http://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Useful representation of 3D rotations.
;;
;;; Code:

(define-module (chickadee math quaternion)
  #:use-module (chickadee math)
  #:use-module (chickadee math vector)
  #:use-module (ice-9 format)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-9 gnu)
  #:use-module (system foreign)
  #:export (quaternion
            quaternion?
            quaternion-w
            quaternion-x
            quaternion-y
            quaternion-z
            make-identity-quaternion
            rotation->quaternion))

(define-record-type <quaternion>
  (wrap-quaternion bv pointer)
  quaternion?
  (bv unwrap-quaternion)
  (pointer quaternion-pointer set-quaternion-pointer!))

(define (quaternion->pointer q)
  "Return a foreign pointer to Q."
  ;; Create foreign pointer lazily.
  (or (quaternion-pointer q)
      (let ((pointer (bytevector->pointer (unwrap-quaternion q))))
        (set-quaternion-pointer! q pointer)
        pointer)))

(define-inlinable (quaternion-ref q i)
  (f32vector-ref (unwrap-quaternion q) i))

(define-inlinable (quaternion-set! q i x)
  (f32vector-set! (unwrap-quaternion q) i x))

(define-syntax-rule (with-new-quaternion name body ...)
  (let ((name (wrap-quaternion (f32vector 0.0 0.0 0.0 0.0) #f)))
    body ... name))

(define-inlinable (quaternion x y z w)
  "Return a new quaternion with values X, Y, Z, and W."
  (with-new-quaternion q
    (quaternion-set! q 0 x)
    (quaternion-set! q 1 y)
    (quaternion-set! q 2 z)
    (quaternion-set! q 3 w)))

(define-inlinable (make-identity-quaternion)
  "Return the identity quaternion."
  (quaternion 0.0 0.0 0.0 1.0))

(define-inlinable (make-null-quaternion)
  (quaternion 0.0 0.0 0.0 0.0))

(define-inlinable (quaternion-x q)
  "Return the X coordinate of the quaternion Q."
  (quaternion-ref q 0))

(define-inlinable (quaternion-y q)
  "Return the Y coordinate of the quaternion Q."
  (quaternion-ref q 1))

(define-inlinable (quaternion-z q)
  "Return the Z coordinate of the quaternion Q."
  (quaternion-ref q 2))

(define-inlinable (quaternion-w q)
  "Return the W coordinate of the quaternion Q."
  (quaternion-ref q 3))

(define (display-quaternion q port)
  (format port "#<quaterion ~f ~f ~f ~f>"
          (quaternion-x q)
          (quaternion-y q)
          (quaternion-z q)
          (quaternion-w q)))

(set-record-type-printer! <quaternion> display-quaternion)

(define-inlinable (quaternion-magnitude q)
  "Return the magnitude of the quaternion Q."
  (let ((w (quaternion-w q))
        (x (quaternion-x q))
        (y (quaternion-y q))
        (z (quaternion-z q)))
    (sqrt (+ (* w w) (* x x) (* y y) (* z z)))))

(define-inlinable (rotation->quaternion axis angle)
  ;; Math taken from here:
  ;; http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
  (let* ((a (/ angle 2.0))
         (s (sin a))
         (c (cos a))
         (n (vec3-magnitude axis)))
    (if (= n 0.0)
        (make-identity-quaternion)
        (quaternion (* (/ (vec3-x axis) n) s)
                    (* (/ (vec3-y axis) n) s)
                    (* (/ (vec3-z axis) n) s)
                    c))))