src/vectors.lisp @ 8119f8eccbed

Episode 46: Kinematics Part 4
author Steve Losh <steve@stevelosh.com>
date Tue, 16 Aug 2016 18:25:48 +0000
parents c72435d307d7
children ae02fff6fa65
(in-package #:coding-math.vectors)


;;;; Utils
(declaim (inline square))


(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun symbolize (&rest args)
    (intern (format nil "~{~A~}" args))))


(defun square (x)
  (* x x))

(defmacro defuns (names &rest rest)
  `(progn
     ,@(loop :for name :in names :collect `(defun ,name ,@rest))))


;;;; Structs
(defmacro defvec (name slots arglist type default)
  `(defstruct (,name (:constructor ,name ,arglist))
     ,@(loop :for slot :in slots :collect `(,slot ,default :type ,type))))

(defmacro defvec2 (name type default)
  `(defvec ,name (x y) (&optional (x ,default) (y x)) ,type ,default))

(defmacro defvec3 (name type default)
  `(defvec ,name (x y z) (&optional (x ,default) (y x) (z y)) ,type ,default))

(defmacro defvec4 (name type default)
  `(defvec ,name (x y z w) (&optional (x ,default) (y x) (z y) (w z)) ,type ,default))


(defvec2 vec2 real 0)
(defvec2 vec2f single-float 0f0)
(defvec2 vec2d double-float 0d0)
(defvec2 vec2i fixnum 0)

(defvec3 vec3 real 0)
(defvec3 vec3f single-float 0f0)
(defvec3 vec3d double-float 0d0)
(defvec3 vec3i fixnum 0)

(defvec4 vec4 real 0)
(defvec4 vec4f single-float 0f0)
(defvec4 vec4d double-float 0d0)
(defvec4 vec4i fixnum 0)


;;;; Operations
(defmacro with-fns (vec-type element-type &body body)
  `(macrolet
    ((vec (&rest args) `(,',vec-type ,@args))
     (vec-x (v) `(,(symbolize ',vec-type '-x) ,v))
     (vec-y (v) `(,(symbolize ',vec-type '-y) ,v))
     (vec-z (v) `(,(symbolize ',vec-type '-z) ,v))
     (vec-w (v) `(,(symbolize ',vec-type '-w) ,v))
     ,(if (eq element-type 'fixnum)
        `(wrap (x) `(logand most-positive-fixnum ,x))
        `(wrap (x) x))
     ,(if (eq element-type 'fixnum)
       `(// (x y) `(floor ,x ,y))
       `(// (x y) `(/ ,x ,y))))
    ,@body))

(defmacro defvec2ops (vec-type element-type)
  (let ((add (symbolize vec-type '-add))
        (sub (symbolize vec-type '-sub))
        (mul (symbolize vec-type '-mul))
        (div (symbolize vec-type '-div))
        (eql (symbolize vec-type '-eql))
        (magdir (symbolize vec-type '-magdir))
        (magnitude (symbolize vec-type '-magnitude))
        (length (symbolize vec-type '-length))
        (angle (symbolize vec-type '-angle))
        (direction (symbolize vec-type '-direction)))
    `(progn
      (declaim (ftype (function (,element-type ,element-type)
                                (values ,vec-type &optional))
                      ,magdir))

      (declaim (ftype (function (,vec-type ,vec-type)
                                (values boolean &optional))
                      ,eql))

      (declaim (ftype (function (,vec-type ,vec-type)
                                (values ,vec-type &optional))
                      ,add ,sub))

      (declaim (ftype (function (,vec-type ,element-type)
                                (values ,vec-type &optional))
                      ,mul ,div))

      (declaim (ftype (function (,vec-type)
                                ,(if (eq 'fixnum element-type)
                                   `(values real &optional)
                                   `(values ,element-type &optional)))
                      ,magnitude ,length ,angle ,direction))

      (with-fns
        ,vec-type ,element-type

        (defun ,magdir (magnitude direction)
          ;; todo figure this out for integer vectors
          (vec
            (* magnitude (cos direction))
            (* magnitude (sin direction))))

        (defun ,eql (v1 v2)
          (and (= (vec-x v1) (vec-x v2))
               (= (vec-y v1) (vec-y v2))))

        (defun ,add (v1 v2)
          (vec
            (wrap (+ (vec-x v1) (vec-x v2)))
            (wrap (+ (vec-y v1) (vec-y v2)))))

        (defun ,sub (v1 v2)
          (vec
            (wrap (- (vec-x v1) (vec-x v2)))
            (wrap (- (vec-y v1) (vec-y v2)))))

        (defun ,mul (v scalar)
          (vec
            (wrap (* (vec-x v) scalar))
            (wrap (* (vec-y v) scalar))))

        (defun ,div (v scalar)
          (vec
            (wrap (// (vec-x v) scalar))
            (wrap (// (vec-y v) scalar))))

        (defuns (,magnitude ,length) (v)
          (sqrt (+ (square (vec-x v))
                   (square (vec-y v)))))

        (defuns (,angle ,direction) (v)
          (atan (vec-y v) (vec-x v)))))))


(defvec2ops vec2 real)
(defvec2ops vec2f single-float)
(defvec2ops vec2d double-float)
(defvec2ops vec2i fixnum)

(declaim (optimize (speed 3)))
; vec2i-add