Created
April 13, 2011 11:39
-
-
Save mariusk/917391 to your computer and use it in GitHub Desktop.
Updated clojure-clr WPF celcius example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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); | |
; } | |
; | |
;; Newbieshly hacked by Marius Kjeldahl to use "LoadWithPartialName" instead of specific version, | |
;; despite this method being "obsoleted" according to docs and created a new gen-func | |
;; macro for compatibility with more recent clojure-clr releases (1.3.0-master-SNAPSHOT dated | |
;; around April 2011). | |
(import '(System.Reflection Assembly)) | |
(Assembly/LoadWithPartialName "PresentationFramework") | |
(Assembly/LoadWithPartialName "PresentationCore") | |
(Assembly/LoadWithPartialName "WindowsBase") | |
(Assembly/LoadWithPartialName "System.Xml") | |
(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)) | |
(defmacro gen-func | |
[& body] | |
`(gen-delegate Action [] ~@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