Skip to content

Instantly share code, notes, and snippets.

@shoover
Created September 25, 2009 22:08
Show Gist options
  • Save shoover/193876 to your computer and use it in GitHub Desktop.
Save shoover/193876 to your computer and use it in GitHub Desktop.
; A CLR port of http://bestinclass.wordpress.com/2009/09/24/chaos-theory-vs-clojure/
; Updated to use Rich Hickey's changes: http://paste.lisp.org/display/87799
;
; A WPF app is fired up in another thread. Anything you type in the REPL
; is dispatched to the WPF thread and evaluated there.
;
; Requires the following addition to ClojureCLR's GenDelegate.cs:
; public static Delegate CreateFunc(IFn fn)
; {
; Type delegateType = typeof(Func<>).MakeGenericType(new Type[] { typeof(object) });
; return Create(delegateType, fn);
; }
(import '(System.Reflection Assembly))
(Assembly/Load "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
(Assembly/Load "PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
(Assembly/Load "WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
(Assembly/Load "System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
(import '(System EventHandler Uri)
'(System.Windows Application Point)
'(System.Windows.Markup XamlReader)
'(System.Windows.Media Color Colors DrawingVisual Pen PixelFormats SolidColorBrush)
'(System.Windows.Media.Imaging RenderTargetBitmap)
'(System.Windows.Threading Dispatcher DispatcherPriority)
'(System.IO File FileStream StringReader)
'(System.Threading ApartmentState Thread ThreadStart)
'(System.Xml XmlReader))
; From ClojureCLR's celsius sample
(defmacro gen-delegate
[type argVec & body]
`(clojure.lang.GenDelegate/Create ~type (fn ~argVec ~@body)))
(defmacro gen-func
[& body]
`(clojure.lang.GenDelegate/CreateFunc (fn [] ~@body)))
(def c-min [-20 -25])
(def c-max [ 25 30])
(def dim-axis (vec (map #(Math/Abs (- %1 %2)) c-min c-max)))
(def dim-screen [800 700])
(def iterations 100)
(def axis-seqs [(vec (take (dim-screen 0)
(iterate #(+ (/ (dim-axis 0) (dim-screen 0)) %) (c-min 0))))
(vec (take (dim-screen 1)
(iterate #(+ (/ (dim-axis 1) (dim-screen 1)) %) (c-min 1))))])
(defn ikeda
[x y u]
(iterate (fn [[x_n y_n]]
(let [t_n (- 0.4 (/ 6 (+ 1 (* x_n x_n) (* y_n y_n))))]
[(inc (* u (- (* x_n (Math/Cos t_n))
(* y_n (Math/Sin t_n)))))
(* u (+ (* x_n (Math/Sin t_n))
(* y_n (Math/Cos t_n ))))]))
[x y]))
(defn ikedas [u cnt]
(take cnt (repeatedly
#(let [x (nth (axis-seqs 0) (rand-int (dim-screen 0)))
y (nth (axis-seqs 1) (rand-int (dim-screen 1)))]
(ikeda x y u)))))
(defn screen-pt [coordinate]
(map #(* (/ (- %1 %2) (- %3 %2)) %4) coordinate c-min c-max dim-screen))
(defn point
[x y]
(Point. (double x) (double y)))
(defn draw-ikeda-map [context n u iks]
(let [point-color (int (+ 155 (* (/ 100 iterations) (- n iterations))))
pen (Pen. (SolidColorBrush. (Color/FromRgb point-color point-color point-color))
0.75)]
(doseq [coords iks]
(let [[x1 y1] (screen-pt (first coords))
[x2 y2] (screen-pt (second coords))]
(.DrawLine context pen (point x1 y1) (point x2 y2))))))
(defn render
[target {:keys [n u iks]}]
(when (zero? n)
(.Clear target))
(let [vis (DrawingVisual.)
context (.RenderOpen vis)]
(draw-ikeda-map context n u iks)
(.Close context)
(.Render target vis)))
(defn animate-1
[amodel u flips]
(loop [n 0 iks (ikedas u 200)]
(when (< n iterations)
(reset! amodel {:n n :u u :flips flips :iks iks})
(Thread/Sleep 20)
(recur (inc n) (map next iks)))))
(defn animate-loop
([amodel u] (animate-loop amodel u 0))
([amodel u flips]
(animate-1 amodel u flips)
(recur amodel (+ u (- 0.05 (/ (rand-int 100) 1000))) (inc flips))))
(def xaml "<Window
xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"
xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"
Title=\"Ikeda Map\" Height=\"744\" Width=\"800\">
<StackPanel Name=\"mainPanel\" Orientation=\"Vertical\">
<StackPanel Orientation=\"Horizontal\">
<TextBlock Name=\"u\" Margin=\"3\" />
<TextBlock Name=\"n\" Margin=\"3\" />
</StackPanel>
<Border BorderBrush=\"Black\" BorderThickness=\"2\" CornerRadius=\"5\">
<Image Name=\"img\" Stretch=\"UniformToFill\" />
</Border>
</StackPanel>
</Window>
")
(def app (atom nil))
(def model (atom nil))
(defn run-wpf-app
"Creates a WPF Application and loads xaml-string as raw XAML. The top-level
element from the xaml is passed to init-fn and then used to run the app."
[xaml-string init-fn]
(reset! app (Application.))
(let [top-level (-> xaml StringReader. XmlReader/Create XamlReader/Load)]
(init-fn top-level)
(.Run @app top-level)))
(defn ui-init
[amodel w]
(let [target (RenderTargetBitmap. (first dim-screen) (last dim-screen) 90 90
PixelFormats/Pbgra32)]
;(.set_WindowState w System.Windows.WindowState/Minimized)
(.set_Source (.FindName w "img") target)
(add-watch amodel :ui ; When the model changes, render the screen
(fn [_ _ _ newval]
(.Invoke (.get_Dispatcher w) DispatcherPriority/Normal
(gen-func
(render target newval)
(.set_Text (.FindName w "u") (format "u: %5f2" (:u newval)))
(.set_Text (.FindName w "n")
(str "iterations: " (+ (* (:flips newval) iterations)
(:n newval))))))))))
(defn wpf-eval
"evals data by invoking an operation to uithread's dispatcher. As a partial function
with the first two args filled, this can be used as the :eval arg for the repl."
[uithread repl-ns-sym data]
(.Invoke (Dispatcher/FromThread uithread) DispatcherPriority/Normal
(gen-func
(clojure.main/with-bindings
(in-ns repl-ns-sym)
(eval data)))))
(let [uithread (doto (Thread. (gen-delegate ThreadStart []
(run-wpf-app xaml (partial ui-init model))))
(.SetApartmentState ApartmentState/STA)
(.Start))]
(doto (Thread. (gen-delegate ThreadStart [] (animate-loop model 0.902)))
(.Start))
(clojure.main/repl :eval (partial wpf-eval uithread 'user)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment