Skip to content
This repository
branch: master
Zach Tellman
file 123 lines (105 sloc) 3.803 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
;; Copyright (c) Zachary Tellman. All rights reserved.
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.

(ns example.gpgpu.mandelbrot
  (:use [penumbra opengl compute])
  (:require [penumbra.app :as app]
            [penumbra.data :as data]))

(defn init [state]

  (app/title! "Mandelbrot Viewer")

  (defmap initialize-fractal
    (float3 (mix upper-left lower-right (/ :coord :dim)) 0))

  (defmap iterate-fractal
    (let [val %
          c (mix upper-left lower-right (/ :coord :dim))]
      (dotimes [i num-iterations]
        (let [z (.xy val)
              iterations (.z val)]
          (<- val
            (if (< 4 (dot z z))
               val
               (float3
                (+ c
                   (float2
                    (- (* (.x z) (.x z)) (* (.y z) (.y z)))
                    (* 2 (.x z) (.y z))))
                (+ 1 iterations))))))
      val))

  (defmap color-fractal
    (let [val %
          z (.xy val)
          n (.z val)
          escape (-> n (- (-> z length log2 log2)) (/ (float max-iterations)))]
      (if (< 4 (dot z z))
         (color3 (mix [0 0 1] [1 1 1] escape))
         (color3 0 0 0))))

  state)

(defn reset-fractal [state]
  (when (:data state)
    (data/release! (:data state)))
  (when (:image state)
    (data/release! (:image state)))
  (assoc state
    :iterations 0
    :image nil
    :data nil))

(defn update-bounds [state]
  (let [[w h] (:dim state)
        center (:offset state)
        radius (map #(/ % (:zoom state)) [(/ (float w) h) 1])
        ul (map - center radius)
        lr (map + center radius)]
    (assoc (reset-fractal state)
      :upper-left ul
      :lower-right lr)))

(defn mouse-down [[x y] button state]
  (let [ul (:upper-left state)
        lr (:lower-right state)
        [nx ny] (map / [x y] (:dim state))]
    (update-bounds
      (assoc state
        :zoom (max 1
                   (* (:zoom state)
                      (if (= button :left) 2 0.5)))
        :offset (map + ul (map * [nx (- 1 ny)] (map - lr ul)))))))

(defn reshape [[x y w h] state]
  (ortho-view 0 1 1 0 -1 1)
  (update-bounds
    (assoc state
      :dim [w h])))

(defn key-press [key state]
  (cond
   (= key :escape) (app/pause!)
   :else state))

(def iterations-per-frame 60)

(defn update [_ state]
  (let [max-iterations (* 20 (Math/pow (:zoom state) 0.5))]
    (if (< (:iterations state) max-iterations)
      (with-frame-buffer
        (let [ul (:upper-left state)
              lr (:lower-right state)
              iters (+ (:iterations state) iterations-per-frame)
              data (or
                       (:data state)
                       (initialize-fractal {:upper-left ul :lower-right lr} (:dim state)))
              next (iterate-fractal {:upper-left ul :lower-right lr :num-iterations iterations-per-frame} data)
              image (color-fractal {:max-iterations max-iterations} [next])]
          (assoc state
            :repaint true
            :iterations iters
            :data next
            :image image)))
      (assoc state
        :repaint false))))

(defn display [_ state]
  (when (:repaint state)
    (app/repaint!))
  (blit! (:image state)))

(defn start []
  (app/start
   {:init init, :reshape reshape, :update update, :display display, :mouse-down mouse-down, :key-press key-press}
   (reset-fractal {:upper-left [-2.0 1.0] :lower-right [1.0 -1.0] :zoom 1 :offset [-0.5 0]})))

Something went wrong with that request. Please try again.