fix canvas pixel writing

add bounds checks prior to writing, might be sub-optimal for
performance, but there are relatively few writes currently

include exercises of "projectiles" tracking via vector math
1 files changed, 55 insertions(+), 18 deletions(-)

M tracer.lisp
M tracer.lisp +55 -18
@@ 70,11 70,10 @@ 
   height
   (body (make-array (list width height) :initial-element (vector 0 0 0))))
 
-;; (defun create-canvas (width height)
-;;   (make-array (list width height) :initial-element (vector 0 0 0)))
-
 (defun write-pixel (canvas x y color)
-  (setf (aref (canvas-body canvas) x y) color))
+  (destructuring-bind (n m) (array-dimensions (canvas-body canvas))
+    (if (and (< x n) (< y m))
+        (setf (aref (canvas-body canvas) x y) color))))
 
 (defun wrap (text width)
   (setq text (concatenate 'string text " "))

          
@@ 91,12 90,15 @@ 
       (push (subseq text offset previous) lines)
       (setq offset (1+ previous)))))
 
+(flatten (ls)
+  (labels ((mklist (x) (if (listp x) x (list x))))
+    (mapcan #'(lambda (x) (if (atom x) (mklist x) (flatten x))) ls)))
+
 (defun canvas-pixel-strings (c)
   (labels ((pixel->string (v) (format nil "~s ~s ~s" (Red v) (Green v) (Blue v))))
-    (loop for pixel across
-         (make-array (apply #'* (array-dimensions (canvas-body c)))
-                     :displaced-to (canvas-body c))
-       collect (pixel->string pixel))))
+    (flatten (loop for i below (canvas-height c) collect
+                  (loop for j below (canvas-width c)
+                     collect (pixel->string (aref (canvas-body c) j i)))))))
 
 (defun canvas->string (c)
   (let* ((triplet-strings (canvas-pixel-strings c))

          
@@ 108,6 110,10 @@ 
   (format nil "P3~%~s ~s~%255~%~a~%"
           (canvas-width c) (canvas-height c) (canvas->string c)))
 
+(defun ppm->file (ppm-string path)
+  (with-open-file (stream path :direction :output :if-exists :supersede)
+    (format stream ppm-string)))
+
 ;;; tests:
 (deftest adding-vec3
   (let ((a (vector 3 -2 5))

          
@@ 189,7 195,7 @@ 
     (check (equalp (cross-product v1 v2) (vector -1 2 -1))
            (equalp (cross-product v2 v1) (vector 1 -2 1)))))
 
-(deftest chapter-1
+(deftest vector-basics
   (adding-vec3)
   (subtracting-vec3)
   (point-minus-a-vector)

          
@@ 224,14 230,15 @@ 
          (h (canvas-height c)))
     (check (equalp w 10)
            (equalp h 20)
-           (loop for i across (make-array (* w h) :displaced-to (canvas-body c))
+           (loop for i across (make-array (array-total-size (canvas-body c))
+                                          :displaced-to (canvas-body c))
               always (equalp i (vector 0 0 0))))))
 
 (deftest canvas-write-pixel
   (let ((canvas (make-canvas :width 10 :height 20))
         (color (vector 1 0 0)))
     (write-pixel canvas 2 3 color)
-    (check (equalp (aref (canvas-body canvas) 2 3) (vector 1 0 0)))))
+    (check (equalp (aref (canvas-body canvas) 3 2) (vector 1 0 0)))))
 
 (deftest check-canvas-pixel-string
   (let ((c (make-canvas :width 1 :height 2)))

          
@@ 254,7 261,7 @@ 0 0 0 0 0 0 0 0 0 0
   (let ((c (canvas->ppm (make-canvas :width 3 :height 5))))
     (check (equalp 0 (position #\Newline (reverse c) :test #'char=)))))
 
-(deftest chapter-2
+(deftest canvas-and-visuals
   (color-vector-getters)
   (blending-colors)
   (canvas-properties)

          
@@ 264,11 271,41 @@ 0 0 0 0 0 0 0 0 0 0
   (output-canvas-to-ppm)
   (ppm-includes-trailing-newline))
 
-(defun ppm->file (ppm-string path)
-  (with-open-file (stream path :direction :output :if-exists :supersede)
-    (format stream ppm-string)))
+(deftest suite
+  (vector-basics)
+  (canvas-and-visuals))
+
+;;; projectile tracking
+(defstruct projectile position velocity)
+(defstruct environment gravity wind)
+
+(defun tick (env proj)
+  (let ((p (VEC3+ (projectile-position proj) (projectile-velocity proj)))
+        (v (VEC3+ (projectile-velocity proj) (VEC3+ (environment-gravity env)
+                                                    (environment-wind env)))))
+    (make-projectile :position p :velocity v)))
 
-(deftest suite
-  (chapter-1)
-  (chapter-2))
+(do* ((env (make-environment :gravity (vector 0 -0.1 0)
+                             :wind (vector -0.01 0 0)))
+      (p (make-projectile :position (vector 0 1 0)
+                          :velocity (normalize (vector 1 1 0)))
+         (tick env p)))
+     ((if (<= (Y (projectile-position p)) 0) (return p)))
+  (format t "~a~%" (projectile-position p)))
 
+;;; projectile visualization
+(do* ((env (make-environment :gravity (vector 0 -0.09 0)
+                             :wind (vector -0.01 0 0)))
+      (p (make-projectile :position (vector 0 1 0)
+                          :velocity (scale-vec3 (normalize (vector 1 1.5 0)) 10))
+         (tick env p))
+      (c (make-canvas :width 900 :height 550)))
+     ((<= (Y (projectile-position p)) 0)
+      (ppm->file (canvas->ppm c) "/home/nolan/test-output.ppm"))
+  (format t "~s ~s~%"
+          (max 0 (floor (X (projectile-position p))))
+          (max 0 (floor (- (canvas-height c) 100))))
+  (write-pixel c
+               (max 0 (floor (X (projectile-position p))))
+               (max 0 (floor (- (canvas-height c) (Y (projectile-position p)))))
+               (vector 255 255 255)))