In my previous post I described how to visualise simple Bézier curves. I chose
Clojure as implementation language. Unfortunately I had to ask you to jump through a couple of hoops in order to code along: installing leiningen, installing and configuring the lein-try plugin and maybe even installing the JVM.
This post will be easier for you - at least in terms of setup. You won’t have to leave your browser and can experiment with
Clojure(Script) right here on this page!
In this post we’re going to use the very useful klipse plugin. Klipse is a client-side code evaluator pluggable on any web page. It can evaluate
Let’s start with a very simple Klipse demonstration. I’m going to
inc(rement) function over the numbers 1, 2 and 3. The result will not surprise you:
(map inc [1 2 3])
What might surprise you though, is that you can change the code in the klipse evaluator and see the results immediately in your browser. So for instance change
(map inc [1 2 3]) into
(map inc [41 999 2]) and be amazed. Or change it into something completely different like
(filter even? (range 10)).
Defining functions and calling them? No problem:
(defn factorial [n] (if (= 1 n) n (* n (factorial (- n 1))))) (factorial 4)
Now call the factorial function with some other natural number (whole number > 0) and see the results.
For those of you who don’t know
Clojure(Script), this function basically says:
nbe its argument
nequals 1, return
factorialof (n - 1)
Lisp in the browser. Embedded in this blog. No setup required. Excellent!
But hang on, better things will follow soon.
Why reinvent your own platform, your own runtime, your own garbage collector, your own ecosystem, when all you need is a decent language?
Clojure is designed to be a hosted language. It runs on:
Clojurescript - a compiler for
In my previous post I tried to explain how (simple) Bézier curves ‘work’. You might want to go there or scan the Wikipedia page on Bézier curves if you have no clue what I’m talking about. Otherwise, let’s dive right in and try to play with Bézier curves directly on this page.
;; Get a grip on the html canvas element I inserted on this page (def canvas (.getElementById js/document "canvas-2d")) (def ctx (.getContext canvas "2d")) ;; Draws a blue circle with radius `r` on the canvas on point [x y] (defn draw-point [x y] (let [r 5] ;; radius (set! (.-fillStyle ctx) "blue") (.beginPath ctx) (.arc ctx x y r 0 (* 2 Math/PI)) (.fill ctx))) ;; Draws a red Bézier curve and its blue control points on the canvas (defn draw-bezier-curve [[x1 y1] [x2 y2] [x3 y3]] ;; draw curve (set! (.-strokeStyle ctx) "red") (set! (.-lineWidth ctx) 2) (.beginPath ctx) (.moveTo ctx x1 y1) (.quadraticCurveTo ctx x2 y2 x3 y3) (.stroke ctx) (.closePath ctx) ;; draw control points (draw-point x1 y1) (draw-point x2 y2) (draw-point x3 y3)) ;; Center the *drawing* canvas within the *html* canvas and draw the curves ;; ;; Reason for centering: keep the coordinates simple AND ;; see the complete shapes (not cropped at the edge of the html canvas) ;; make the ratio 1 to see what I mean with 'cropped at the edge' ;; (let [wc (.-width canvas) ;; width of *html* canvas hc (.-height canvas) ;; height of *html* canvas ratio 0.9 ;; ratio of *html* canvas to use as *drawing* canvas t (/ (- 1 ratio) 2) ;; translation constant w (* ratio wc) ;; width of *drawing* canvas h (* ratio hc) ;; height of *drawing* canvas x (* 1/2 w) ;; x val in the middle of 0 and w y (* 1/2 h)] ;; y val in the middle of 0 and h ;; clear *html* canvas (.clearRect ctx 0 0 wc hc) ;; Center the *drawing* canvas in the *html* canvas (.save ctx) (.translate ctx (* t wc) (* t hc)) ;; Draw a grey border around the *drawing* canvas (set! (.-lineWidth ctx) 1) (set! (.-strokeStyle ctx) "grey") (.strokeRect ctx 0 0 w h) ;; Draw main 'anchor' points ;; This is what I meant with 'simple coordinates': ;; (draw-point 0 0) ;; (draw-point x y) ;; (draw-point w h) ;; Increasing ascending curve (draw-bezier-curve [0 h] [w h] [w 0]) ;; Swoosh ;; (draw-bezier-curve [0 y] [0 h] [w 0]) ;; normal curve ;; (draw-bezier-curve [0 h] [x 0] [w h]) ;; my pulse after a useless meeting ;; (draw-bezier-curve [0 y] [x y] [w y]) (.restore ctx))
Before reading further you might want to experiment a bit by (un)commenting code and seeing the results. (Un)comment the different Bézier curves and different control points in the Klipse evaluator and see what happens.
Scanning the code quickly without going into detail, this is what happens:
Clojure is a hosted language, it must be able to access its host platform and libraries.
ClojureScript have good interop documentation so I won’t go into detail here, but we’re basically using these forms in this blog:
js/document=> the global document object
The last form is used for instance in
(set! (.-lineWidth ctx) 1) and translates to
ctx.lineWidth = 1. The more general syntax is
(set! var-symbol expr).
The second form
(.beginPath ctx) can also have arguments. The general syntax is
(.instanceMember instance args*) in that case.
Other than that it’s basic
Clojure and HTML Canvas functionality
Thanks to the klipse plugin and a bit of preparation from my side, you can now play around with Bézier Curves in
ClojureScript directly in this blog. I do realise this post is probably not a compelling case for using
ClojureScriptfits large browser applications better, where you need sane state management, immutable datastructures, lazy sequences, and a fast, stable and robust language
I’m not interested in ‘religious’ discussions about technology A versus technology B.
ClojureScript are THE sane way forward for me, in my context. I hope to share the fun I’m experiencing with it. Goethe said it best:
It is always better to say right out what you think without trying to prove anything much: for all our proofs are only variations of our opinions, and the contrary-minded listen neither to one nor the other.
But with all this talk about language, we’re almost forgetting what it’s all about: building useful stuff and having a great time doing it.
Thanks Niek for sharing the 040code repo with me. Please share your comments, suggestions and thoughts about this blog post on twitter.com/mmz_. Thanks for reading and Happy Coding!