Ray Tracing In One Weekend (in Lisp, and n-dimenions) September 26th, 2024
Patrick Stein

Earlier this year, I started working through the online book Ray Tracing In One Weekend (Book 1). I have been following along with it in Common Lisp, and I have been extending it all from 3-dimensional to n-dimensional.

I reproduced 4-dimensional versions of all of the book images which you can see on my weekend-raytracer github page.

Here is the final image. This is a 250-samples-per-pixel, 640x360x10 image plane of three large hyperspheres (one mirrored, one diffuse, one glass) atop a very large, diffuse hypersphere. Also atop this very large hypersphere are a bunch of smaller hyperspheres of varying colors and materials. The image is rendered with some defocus-blur.

Image described in detail in post.
Final image of 4-dimensional scene

Caveat: This depends on a patched version of the policy-cond library that is not in the current Quicklisp distribution but should be in the next.

Track-Best Library Released May 9th, 2013
Patrick Stein

In doing a problem set from the Internet a few weeks ago, I found myself writing awkward constructions with REDUCE and LOOP to try to find the best one (or two or three) things in a big bag of things based on various criteria: similarity to English, hamming distance from their neighbor, etc.

I wrote a macro that encapsulated the pattern. I’ve reworked that macro into a library for public use.

Acquiring

Using

There are a variety of examples in the README and the tests directory.

Here is one example to pique your interest. Suppose you have some data about the elevations of various cities in various states and you (being a Moxy Fruvous fan) want to know What Is the Lowest Highest Point? Here’s how you might tackle that with the TRACK-BEST library:

(let ((data '(("Alabama"  ("Birmingham" 664)
                          ("Mobile" 218)
                          ("Montegomery" 221))
              ("Alaska"   ("Anchorage" 144)
                          ("Fairbanks" 531))
              ("Arizona"  ("Grand Canyon" 6606)
                          ("Phoenix" 1132)
                          ("Tuscon"  2641)))))
  (with-track-best (:order-by-fn #'<)
    (dolist (state-info data)
      (multiple-value-bind (city altitude)
          (with-track-best ()
            (dolist (city-info (rest state-info))
              (track (first city-info) (second city-info))))
        (track (list (first state-info) city) altitude)))))

With this limited dataset, the end result would be (VALUES '("Alaska" "Fairbanks") 531). The inner WITH-TRACK-BEST finds the highest city in each state. The outer WITH-TRACK-BEST finds the lowest of these.

USerial — v0.8.2011.06.02 June 2nd, 2011
Patrick Stein

I am releasing a new version of the USerial library. New in this version:

  • Fix (make-int-serializer) to be big-endian 2’s complement
  • Add :symbol and :keyword serializers
  • Add (make-vector-serializer)
  • Add (make-key-slot-serializer) and (make-key-accessor-serializer)
  • Add (define-serializing-funcall)

Obtaining

USerial — v0.7.2011.05.24 May 24th, 2011
Patrick Stein

I am releasing a new version of my USerial library. This version cleans up many messes from earlier releases. Unfortunately, in that process, it breaks compatibility with earlier releases.

Obtaining

Getting the USerial library:

Differences

The differences between this version and earlier versions of this library include:

  • Use of ContextL layered functions instead of CLOS methods
  • Elimination of :buffer parameter in favor of using the *buffer* special variable
  • Cleaning up macros which no longer required the :buffer parameter
  • Serializers for arbitrarily large integers and unsigned integers
  • Serializer for raw sequence of bytes
  • New make-list-serializer macro

By using ContextL layered functions, one has the ability to define a serializer and/or unserializer in a particular ContextL layer. This can be used to create new versions of the serializer without losing the ability to use the older version when required.

In the process, I have created macros to assist in creating completely custom serializers. This both streamlines their definition and should allow any future modifications to the USerial library to fly under the radar. Code that before looked like this:

(defmethod serialize ((key (eql :foo)) (value foo-struct)
                      &key (buffer userial:*buffer*) &amp;allow-other-keys)
  ... some code ...
  buffer)

(defmethod unserialize ((key (eql :foo))
                        &key (buffer userial:*buffer*) &amp;allow-other-keys)
  (values (progn ... some code ...)
          buffer))

Should now look like this:

(define-serializer (:foo (value foo-struct))
  ... some code ...)

(define-unserializer (:foo)
  ... some code ...)

And, when you find you need to add a new version of your :foo serializer but you don’t want to lose the old one, you can add:

(contextl:deflayer new-version)

(define-serializer (:foo (value foo-struct) :layer new-version)
  ... some new code ...)

Without the :buffer parameter everywhere, code that used to look like this:

(serialize* (:string aa :uint8 bb) :buffer buf)
(buffer-rewind :buffer buf)
(unserialize-slots* (:string name :uint8 age) object :buffer buf)

Should now look like this:

(with-buffer buf
  (serialize* :string aa :uint8 bb)
  (buffer-rewind)
  (unserialize-slots* object :string name :uint age))

There are now :int and :uint serializers that encode arbitrarily large integers and unsigned integers, respectively. There is also a serializer that copies a sequence of bytes as is without any prefix or suffix. To unserialize, you either have to provide a buffer of the appropriate length with the :output parameter or provide appropriate :start and :end keywords.

(serialize :raw-bytes uchar-array &key (start 0)
                                       (end (length uchar-array)))
(unserialize :raw-bytes &key output
                             (start 0)
                             (end (length output)))

And, if you have a serialize/unserialize pair for type :foo you can use the make-list-serializer macro to create a serialize/unserialize pair for a list of items that can be serialized with the :foo serializer.

(make-list-serializer :list-of-uint8 :uint8)
(serialize :list-of-int8 '(0 1 1 2 3 5 8 13 21 34 55 89))

At the USerial home page, you can find more complete documentation.

USerial — v0.7.2011.05.24 May 24th, 2011
Patrick Stein

I am releasing a new version of my USerial library. This version cleans up many messes from earlier releases. Unfortunately, in that process, it breaks compatibility with earlier releases.

Obtaining

Getting the USerial library:

Differences

The differences between this version and earlier versions of this library include:

  • Use of ContextL layered functions instead of CLOS methods
  • Elimination of :buffer parameter in favor of using the *buffer* special variable
  • Cleaning up macros which no longer required the :buffer parameter
  • Serializers for arbitrarily large integers and unsigned integers
  • Serializer for raw sequence of bytes
  • New make-list-serializer macro

By using ContextL layered functions, one has the ability to define a serializer and/or unserializer in a particular ContextL layer. This can be used to create new versions of the serializer without losing the ability to use the older version when required.

In the process, I have created macros to assist in creating completely custom serializers. This both streamlines their definition and should allow any future modifications to the USerial library to fly under the radar. Code that before looked like this:

(defmethod serialize ((key (eql :foo)) (value foo-struct)
                      &key (buffer userial:*buffer*) &amp;allow-other-keys)
  ... some code ...
  buffer)

(defmethod unserialize ((key (eql :foo))
                        &key (buffer userial:*buffer*) &amp;allow-other-keys)
  (values (progn ... some code ...)
          buffer))

Should now look like this:

(define-serializer (:foo (value foo-struct))
  ... some code ...)

(define-unserializer (:foo)
  ... some code ...)

And, when you find you need to add a new version of your :foo serializer but you don’t want to lose the old one, you can add:

(contextl:deflayer new-version)

(define-serializer (:foo (value foo-struct) :layer new-version)
  ... some new code ...)

Without the :buffer parameter everywhere, code that used to look like this:

(serialize* (:string aa :uint8 bb) :buffer buf)
(buffer-rewind :buffer buf)
(unserialize-slots* (:string name :uint8 age) object :buffer buf)

Should now look like this:

(with-buffer buf
  (serialize* :string aa :uint8 bb)
  (buffer-rewind)
  (unserialize-slots* object :string name :uint age))

There are now :int and :uint serializers that encode arbitrarily large integers and unsigned integers, respectively. There is also a serializer that copies a sequence of bytes as is without any prefix or suffix. To unserialize, you either have to provide a buffer of the appropriate length with the :output parameter or provide appropriate :start and :end keywords.

(serialize :raw-bytes uchar-array &key (start 0)
                                       (end (length uchar-array)))
(unserialize :raw-bytes &key output
                             (start 0)
                             (end (length output)))

And, if you have a serialize/unserialize pair for type :foo you can use the make-list-serializer macro to create a serialize/unserialize pair for a list of items that can be serialized with the :foo serializer.

(make-list-serializer :list-of-uint8 :uint8)
(serialize :list-of-int8 '(0 1 1 2 3 5 8 13 21 34 55 89))

At the USerial home page, you can find more complete documentation.

Updates In Email

Email:

l