The plasma effect is a visual effect created by applying various functions, notably sine and cosine, to the color values of screen pixels. When animated (not a task requirement) the effect may give the impression of a colorful flowing liquid.



Task
Create a plasma effect.




See also
 Computer Graphics Tutorial (lodev.org)
 Plasma (bidouille.org)


(require :lispbuilder-sdl)
(require :simple-rgb)
 
(defparameter *palette*
  (let ((palette-aux (make-array 256 :element-type 'fixnum)))
    (dotimes (i 256)
      (let ((color_i (simple-rgb:hsv->rgb (simple-rgb:hsv (/ i 255.0) 1.0 1.0))))
        (setf (aref palette-aux i) (loop :for component :across color_i
                                       :for i :from 0
                                       :sum (ash component (* 8 i))))))
    palette-aux)
  "palette")
 
(defun value->color (palette palette-shift index)
  (aref palette (mod (+ index palette-shift) (length palette))))
 
(defun return-color-by-pos (x y &optional w h)
  "returns a color index"
  (floor
   (/ (+ (+ 128.0 (* 128.0 (sin (/ x 16.0))))
         (+ 128.0 (* 128.0 (sin (/ y 8.0))))
         (+ 128.0 (* 128.0 (sin (/ (+ x y) 16.0))))
         (+ 128.0 (* 128.0 (sin (/ (sqrt (+ (* x x) (* y y))) 8.0)))))
      4.0)))
 
(defun return-color-by-pos-another (x y &optional w h)
  "a different function that returns a color index"
  (floor
   (/ (+ (+ 128.0 (* 128.0 (sin (/ x 16.0))))
         (+ 128.0 (* 128.0 (sin (/ y 32.0))))
         (+ 128.0 (* 128.0 (sin (/ (sqrt (+ (expt (/ (- x w) 2.0) 2) (expt (/ (- y h) 2.0) 2))) 8.0))))
         (+ 128.0 (* 128.0 (sin (/ (sqrt (+ (* x x) (* y y))) 8.0)))))
      4.0)))
 
(defun plasma-render (surface palette-shift)
  "render plasma"
  (let ((width (sdl:width surface))
        (height (sdl:height surface)))
    (sdl-base::with-pixel (s (sdl:fp surface))
      (dotimes (h height)
        (dotimes (w width)
          (sdl-base::write-pixel s w h (value->color *palette* palette-shift (funcall #'return-color-by-pos-another w h width height)))))))
  surface)
 
(defun demo/plasma ()
  "main function: shows a window rendering a plasma efect"
  (sdl:with-init ()
    (let ((win (sdl:window 320 240
                           :bpp 24
                           :resizable nil
                           :title-caption "demo/plasma"
                           :icon-caption "demo/plasma")))
      (let ((palette-shift 0))
      (sdl:update-display win)
      (sdl:with-events ()
        (:idle
         (plasma-render win palette-shift)
         (sdl:update-display win)
         (incf palette-shift))
        (:video-expose-event () (sdl:update-display win))
        (:key-down-event (:key key)
                         (when (or
                                (sdl:key= key :sdl-key-escape)
                                (sdl:key= key :sdl-key-q))
                           (sdl:push-quit-event)))
        (:quit-event () t))))))
 
(demo/plasma)