Skip to content

Commit 2f366d9

Browse files
committed
Convert logo example to SVG, which removes dependency on clojure2d.
1 parent 47e95fd commit 2f366d9

File tree

2 files changed

+69
-47
lines changed

2 files changed

+69
-47
lines changed

deps.edn

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
io.github.jackrusher/mundaneum {:git/sha "d2c934a12388d88ddb3e53fef92ec2eef97d6140"}
2424
arrowic/arrowic {:mvn/version "0.1.1"}
2525

26-
2726
;; OSC server
2827
com.illposed.osc/javaosc-core {:mvn/version "0.8" :exclusions [org.slf4j/slf4j-api org.slf4j/slf4j-log4j12]}
2928

@@ -35,7 +34,6 @@
3534
:aliases {:nextjournal/clerk {:extra-paths ["datasets"]
3635
:exec-fn nextjournal.clerk/build!
3736
;; notebooks to be built, accepts globs patterns
38-
:nextjournal.clerk/aliases [:clojure2d]
3937
:exec-args {:git/url "https://github.com/nextjournal/clerk-demo"
4038
:paths ["index.md"
4139
"notebooks/slideshow.md"
@@ -47,5 +45,4 @@
4745
"notebooks/sicmutils.clj"
4846
"notebooks/rule_30.clj"
4947
"notebooks/zipper_with_scars.clj"]}
50-
:main-opts ["-m" "babashka.cli.exec"]}
51-
:clojure2d {:extra-deps {clojure2d/clojure2d {:mvn/version "1.4.4"}}}}}
48+
:main-opts ["-m" "babashka.cli.exec"]}}}

notebooks/logo.clj

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
;; # 🎨 Making a Clerk Logo
22
^{:nextjournal.clerk/visibility {:code :fold}}
33
(ns logo
4-
"A notebook generating the Clerk's logo.
5-
6-
Note that to run this, the `:clojure2d` needs to be added."
7-
(:require [nextjournal.clerk :as clerk]
8-
[clojure2d.core :as c2d]
9-
[fastmath.complex :as complex]
10-
[fastmath.vector :as v]))
11-
12-
^{:nextjournal.clerk/visibility {:code :hide :result :hide}}
13-
(System/setProperty "java.awt.headless" "true")
4+
"A notebook generating the Clerk's logo."
5+
(:require [nextjournal.clerk :as clerk]))
146

157
;; The new Clerk header image is made from a fifth order [Hilbert
168
;; Curve](https://en.wikipedia.org/wiki/Hilbert_curve), so we will
@@ -21,8 +13,8 @@
2113
(hilbert-curve 0 0 x-size 0 0 y-size order))
2214
([x y xi xj yi yj n]
2315
(if (<= n 0)
24-
[(v/vec2 (+ x (/ (+ xi yi) 2.0))
25-
(+ y (/ (+ xj yj) 2.0)))]
16+
[[(+ x (/ (+ xi yi) 2.0))
17+
(+ y (/ (+ xj yj) 2.0))]]
2618
(mapcat (partial apply hilbert-curve)
2719
[[x y (/ yi 2.0) (/ yj 2.0) (/ xi 2.0) (/ xj 2.0) (dec n)]
2820
[(+ x (/ xi 2.0)) (+ y (/ xj 2.0)) (/ xi 2.0) (/ xj 2.0) (/ yi 2.0) (/ yj 2.0) (dec n)]
@@ -33,43 +25,76 @@
3325
(def hilbert-points
3426
(hilbert-curve 800 800 5))
3527

36-
;; But they're much more interesting if we use
37-
;; [Clojure2D](https://github.com/Clojure2D/clojure2d) canvas to draw
38-
;; a path made from points to show the complete curve:
39-
(c2d/with-canvas-> (c2d/canvas 800 800 :highest)
40-
(c2d/set-background 255 255 255)
41-
(c2d/set-color 66 66 66)
42-
(c2d/set-stroke 4)
43-
(c2d/path hilbert-points)
44-
c2d/to-image)
28+
;; But they're much more interesting if we use the browser's built-in
29+
;; support for SVG to draw a path made from points to show the
30+
;; complete curve. First, we'll make a little helper function to
31+
;; convert a sequence of points into an SVG path:
32+
33+
(defn points->path
34+
"Turn a sequence of points into an SVG path string."
35+
[[[start-x start-y] & pts]]
36+
(reduce str
37+
(str "M " start-x "," start-y)
38+
(map (fn [[x y]] (str " L" x "," y)) pts)))
39+
40+
;; And then we'll use that to visualize the curve:
41+
42+
(clerk/html
43+
[:svg {:stroke "#666666"
44+
:stroke-width 4
45+
:fill "none"
46+
:viewBox "0 0 800 800"}
47+
[:path {:d (points->path hilbert-points)}]])
4548

4649
;; The trick to getting the effect we want is to apply a conformal
4750
;; mapping to the original Hilbert Curve to convert it into an 👁 shape
4851
;; in celebration of Clerk's viewers. We can do this by treating the
4952
;; original point coordinates as complex numbers, squaring them, then
5053
;; taking the real and imaginary portions of each of those complex
51-
;; numbers as the _x_ and _y_ coordinates of a new set of points. This
52-
;; is made especially easy because Clojure2D happens to include the
53-
;; author's [Fastmath](https://github.com/generateme/fastmath)
54-
;; library. 🎉
55-
56-
(c2d/with-canvas-> (c2d/canvas 1000 600 :highest)
57-
(c2d/set-background 33.0 5.0 24.0) ; RGB deep purple
58-
(c2d/translate 500 300) ; origin to center
59-
(c2d/rotate (/ Math/PI 2)) ; rotate the canvas, ⬯ → ⬭
60-
;; colour and stroke width
61-
(c2d/set-color 147.0 189.0 154.0)
62-
(c2d/set-stroke 4)
63-
;; ellipses to fill in the center of the "eye"
64-
(c2d/ellipse 0 0 22 22)
65-
(c2d/ellipse 0 -10 20 20)
66-
(c2d/ellipse 0 10 20 20)
67-
;; draw a path using the complex square of our hilbert curve points
68-
(c2d/path (map #(-> (v/sub % (v/vec2 400 400)) ; -[½w ½h] from vectors to center the curve
69-
complex/sq ; square each vector as a complex number
70-
(v/mult 0.0015)) ; scale those squared vectors down
71-
hilbert-points))
72-
c2d/to-image)
54+
;; numbers as the _x_ and _y_ coordinates of a new set of points. To
55+
;; make this work, we'll use a couple of helper functions to perform
56+
;; those calculations:
57+
58+
(defn complex-multiply
59+
"Multiply two complex numbers."
60+
[z1 z2]
61+
[(- (* (first z1) (first z2))
62+
(* (second z1) (second z2)))
63+
(+ (* (first z1) (second z2))
64+
(* (second z1) (first z2)))])
65+
66+
(defn complex-square
67+
"Square a complex number."
68+
[z]
69+
(complex-multiply z z))
70+
71+
;; And a helpers for multiplying a vector by a scalar:
72+
73+
(defn v*
74+
"Multiply vector `v` by scalar `s`."
75+
[v s]
76+
[(* (first v) s) (* (second v) s)])
77+
78+
;; After which we can generate the complete logo:
79+
80+
(clerk/html
81+
[:svg {:stroke "rgb(147.0 189.0 154.0)"
82+
:stroke-width 4
83+
:fill "none"
84+
:viewBox "0 0 1000 600"}
85+
[:rect {:width 1000 :height 600 :stroke "none" :fill "rgb(33.0 5.0 24.0)"}] ; deep purple bg
86+
[:ellipse {:cx 500 :cy 300 :rx 11 :ry 11 :stroke "none" :fill "rgb(147.0 189.0 154.0)"}] ; the pupil
87+
[:ellipse {:cx 490 :cy 300 :rx 11 :ry 11 :stroke "none" :fill "rgb(147.0 189.0 154.0)"}]
88+
[:ellipse {:cx 510 :cy 300 :rx 11 :ry 11 :stroke "none" :fill "rgb(147.0 189.0 154.0)"}]
89+
(let [
90+
points (map #(-> [(- (first %) 400) (- (second %) 400)] ; -[½w ½h] to center the curve
91+
complex-square ; square each vector as a complex number
92+
(v* 0.0015)) ; scale those squared vectors down
93+
hilbert-points)]
94+
[:path {:transform "rotate(90) translate(300,-500)" ; rotate and center "eye"
95+
:stroke-linejoin "round"
96+
:stroke-linecap "round"
97+
:d (points->path points)}])])
7398

7499
;; What I find so special and enchanting about the $$w = z^{2}$$
75100
;; mapping that we're using here is that it maintains the angle of

0 commit comments

Comments
 (0)