git.fiddlerwoaroof.com
3dr.lisp
f8737faa
 ;;;; 3dr.lisp
 
 (in-package #:3dr)
 
 ;;; "3dr" goes here. Hacks and glory await!
 
 (defstruct (point (:type vector)) x y z)
 
 (defclass plane ()
   ((cells :initarg :cells :accessor cells :type (array integer (* *)))
    (distance :initarg :distance :accessor distance :type integer)))
 
30f5111d
 ;;(declaim (ftype (function ((vector integer 3) integer) (vector rational 3)) intercept-coordinate))
f8737faa
 (defun intercept-coordinate (point distance &optional out-point)
30f5111d
   (declare (inline) (optimize (speed 3)))
f8737faa
   (when (null out-point)
30f5111d
                                         ;(break)
f8737faa
     (setf out-point (vector 0 0 0)))
   (let ((a (elt point 0)) (b (elt point 1)) (c (elt point 2)))
     (setf (elt out-point 0) distance)
     (setf (elt out-point 1) (/ (* b distance) a))
     (setf (elt out-point 2) (/ (* c distance) a))
     out-point))
 
 (defun run-plane (plane point-cb)
30f5111d
   (declare (optimize (speed 3)))
f8737faa
   (let ((a (distance plane))
         (points (cells plane)))
     (destructuring-bind (b-bound c-bound) (array-dimensions points)
       (lparallel:pdotimes (b (1- b-bound))
         (let ((the-point (make-point :x a :y (- b (/ b-bound 2)) :z 0))
               (out-point (make-point :x 0 :y 0 :z 0)))
           (dotimes (c (1- c-bound))
             (setf (point-z the-point) (- c (/ c-bound 2)))
             (setf (aref points b c)
                   (funcall point-cb the-point out-point))))))))
 
 (declaim (ftype (function ((vector rational 3) (vector rational 3)) float) c-distance))
 (defun c-distance (point-1 point-2)
   ;(declare (optimize (speed 0) (safety 3) (debug 3)))
   (when (not (and (point-x point-1) (point-x point-2)))
     (break))
 
   ;(declare (inline) (optimize (speed 3) (safety 1) (debug 0) (space 0)))
   (sqrt (+ (expt (- (aref point-2 0) (aref point-1 0)) 2)
            (expt (- (aref point-2 1) (aref point-1 1)) 2)
            (expt (- (aref point-2 2) (aref point-1 2)) 2))))
 
 (defun extract-projected-point (point obj-dist &optional out-point)
   ;(declare (inline) (optimize (speed 3)))
   (intercept-coordinate point obj-dist out-point))
 
 (defclass shape ()
   ((origin :initarg :origin :accessor origin)
    (radius :initarg :radius :accessor radius)
    (args :initarg :args :accessor args)
    (render-func :initarg :render-func :accessor render-func)))
 
 (defgeneric get-render-cb (shape))
 (defmethod get-render-cb ((shape shape))
   (with-slots (origin radius render-func args) shape
     (apply render-func origin radius args)))
 
 (defun make-neg-sphere-cb (origin radius)
   (let ((obj-dist (point-x origin))
         (r_sqr (expt radius 2))) 
     (lambda (point &optional out-point extracted-point)
       ;(declare (inline) (optimize (speed 3)))
       (let* ((point (or extracted-point (extract-projected-point point obj-dist out-point)))
              (d_o (c-distance origin point)))
         (if (<= d_o radius)
           (* -1 (ceiling (sqrt (- r_sqr (expt d_o 2))))) 
           0)))))
 
 ;; d_o + r : upright :: upright : r - d_o
 ;; upright^2 = r^2 - d_o^2
 ;; upright = sqrt ( r^2 - d_o^2 )
 (defun make-sphere-cb (origin radius)
   (let ((obj-dist (point-x origin))
         (r_sqr (expt radius 2)))
     (lambda (point &optional out-point extracted-point)
a2d587f2
       (declare (inline) (optimize (speed 3)))
f8737faa
       (let* ((out-point (or out-point (make-point :x 0 :y 0 :z 0)))
              (point (or extracted-point (extract-projected-point point obj-dist out-point))))
         (let ((d_o (c-distance origin point)))
           (values (if (<= d_o radius)
30f5111d
                       (ceiling (* (/ (sqrt (- r_sqr
                                               (expt d_o
                                                     2)))
                                      radius)
                                   #. (expt 2 7))) 
                       0)
f8737faa
                   point))))))
 
a2d587f2
 (defun make-interesting-cb (origin radius)
   (let ((obj-dist (point-x origin))
         (r_sqr (expt radius 3)))
     (lambda (point &optional out-point extracted-point)
       (declare (inline) (optimize (speed 3)))
       (let* ((out-point (or out-point (make-point :x 0 :y 0 :z 0)))
              (point (or extracted-point (extract-projected-point point obj-dist out-point))))
         (let ((d_o (c-distance origin point)))
           (values (if (<= d_o radius)
                       (ceiling (* (/ (expt (- r_sqr
                                               (expt d_o
                                                     3))
                                            0.33)
                                      radius)
                                   #. (expt 2 7))) 
                       0)
                   point))))))
 
f8737faa
 (defun make-donut-cb (origin radius width)
   (let ((pos-cb (make-sphere-cb origin radius))
         (neg-cb (make-neg-sphere-cb origin (- radius width)))
         (obj-dist (point-x origin)))
     (lambda (point &optional out-point)
a2d587f2
       (declare (inline) (optimize (speed 3)))
f8737faa
       (let* ((out-point (or out-point (make-point :x 0 :y 0 :z 0))) 
              (extracted-point (extract-projected-point point obj-dist out-point)))
         (+ (funcall pos-cb point out-point extracted-point)
            (funcall neg-cb point out-point extracted-point))))))
 
 (defun combine-cbs (&rest cbs)
   (lambda (point &optional out-point)
     (let ((out-point (or out-point (make-point :x 0 :y 0 :z 0))))
       (loop for cb in cbs sum (funcall cb point out-point)))))
 
 (defun combine-shapes (&rest shapes)
   (let* ((shape-cbs (mapcar #'get-render-cb
                             (lparallel:psort shapes #'<
                                              :key (lambda (shape)
                                                     (with-slots (origin radius) shape
                                                       (-
                                                         (c-distance #(0 0 0)
                                                                     origin)
                                                         radius))))))
          (origins (mapcar 'origin shapes))
          (radii (mapcar 'radius shapes))
          (light-scaling-factor (lparallel:pmapcar
                                  (lambda (origin radius)
                                    (abs
                                      (/ (- (c-distance #(0 100 100)
                                                        origin)
                                            radius)
                                         100)))
                                  origins
                                  radii)))
     (lambda (point &optional out-point)
       (let ((out-point (or out-point (make-point :x 0 :y 0 :z 0))))
         (loop for shape-cb in shape-cbs
               for scale-factor in light-scaling-factor
               for val = (ceiling
                           (* scale-factor
                              (funcall shape-cb point out-point)))
               until (< 0 val)
               finally (return val))))))
 
 (defun array-to-pgm (arr)
30f5111d
   ;;(declare (optimize (speed 3)))
f8737faa
   (with-output-to-string (s)
     (destructuring-bind (height width) (array-dimensions arr)
       (format s "P2~%~3d ~3d~%~d~%"
               width height
               (1+ (reduce #'max (make-array (* width height) :displaced-to arr)))))
     (loop with (x-bound y-bound) = (array-dimensions arr)
30f5111d
        for x from 0 to (1- x-bound)
        do (loop for y from 0 to (1- y-bound)
              for val = (aref arr x y)
              do (princ val s)
              do (princ #\space s))
        do (terpri s))))
f8737faa
 
 (defmacro defshape (render-func origin radius &rest args)
   `(make-instance 'shape
                   :render-func (function ,render-func)
                   :origin (copy-seq ,origin)
                   :radius ,radius
                   :args (list ,@args)))
 
 (defun main (&rest args)
   (declare (ignore args))
a2d587f2
   ;;(sb-profile:profile intercept-coordinate c-distance)
30f5111d
   (loop for img-num from 0 to 1
      do
        (let ((the-plane (make-instance 'plane
                                        :cells (make-array '(1024 1024))
                                        :distance 50))
              (lparallel:*kernel* (lparallel:make-kernel 7)))
 
          (labels ((100- (x) (- x 100))
                   (random-coord () (100- (random 200))))
            (time
             (run-plane the-plane
                        (apply #'combine-shapes
                               (loop for x from 0 to 100 by 20
                                  append (loop for y from 0 to 100 by 20
                                            collect
                                              (defshape make-sphere-cb
                                                  (vector 30 x y)
                                                10)))))))
 
          (with-open-file (s (format nil "/tmp/spheres.~d.pgm" img-num)
                             :direction :output :if-exists :supersede)
            (write-sequence (array-to-pgm (cells the-plane))
                            s))
          nil))
                                         ;(sb-profile:report)
f8737faa
   )
 
 ;(loop with (x-bound y-bound) = (array-dimensions (cells the-plane))
 ;      for x from 0 to (1- x-bound)
 ;      do (loop for y from 0 to (1- y-bound)
 ;               for val = (aref (cells the-plane) x y)
 ;               when (= val 0) do (princ #\space)
 ;               unless (= val 0) do (princ val)
 ;               do (princ #\space))
 ;      do (terpri))