Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# keymap-viz: visualisation of keymaps

This is a Clojure program for generating visualisations of the keymaps of my keyboards, heavily inspired by [callum-oakley/keymap](https://github.com/callum-oakley/keymap) and the various spins on it.

![](examples/svg/split_3x5_2.svg)
13 changes: 13 additions & 0 deletions env/dev/src/user.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(ns user
"Userspace functions for local development."
(:require [mount.core :as m]))

(defn start []
(m/start))

(defn stop []
(m/stop))

(defn restart []
(stop)
(start))
7 changes: 7 additions & 0 deletions env/test/resources/keymaps/3x3.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{:layers [{:keymap {:all [1 2 3
4 5 6
7 8 9]}}

{:keymap {:all [1 2 3
4 5 6
7 8 9]}}]}
10 changes: 10 additions & 0 deletions env/test/resources/layouts/3x3.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{:blocks {:all {:pos [0 0]
:keys [{:pos [0 0] :size 1 :type :rect}
{:pos [0 1] :size 1 :type :rect}
{:pos [0 2] :size 1 :type :rect}
{:pos [1 0] :size 1 :type :rect}
{:pos [1 1] :size 1 :type :rect}
{:pos [1 2] :size 1 :type :rect}
{:pos [2 0] :size 1 :type :rect}
{:pos [2 1] :size 1 :type :rect}
{:pos [2 2] :size 1 :type :rect}]}}}
10 changes: 10 additions & 0 deletions env/test/resources/layouts/split_2x2.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{:blocks {:left {:pos [0 0]
:keys [{:pos [0 0] :size 1 :type :rect}
{:pos [0 1] :size 1 :type :rect}
{:pos [1 0] :size 1 :type :rect}
{:pos [1 1] :size 1 :type :rect}]}
:right {:pos [3 0]
:keys [{:pos [0 0] :size 1 :type :rect}
{:pos [0 1] :size 1 :type :rect}
{:pos [1 0] :size 1 :type :rect}
{:pos [1 1] :size 1 :type :rect}]}}}
10 changes: 10 additions & 0 deletions env/test/src/test_utils.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(ns test-utils
(:require
[clojure.edn :as edn]
[clojure.java.io :as io]))

(defn load-edn-file [filename]
(->> (io/resource filename)
(slurp)
(edn/read-string)
(eval)))
105 changes: 98 additions & 7 deletions examples/keymaps/split_3x5_2.edn
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,105 @@
:border-radius 6}

:layers [{:name "Colemak-DH base"
:keymap {:left ["Q" "W" "F" "P" "B"
"A" "R" "S" "T" "G"
"Z" "X" "C" "D" "V"]
:keymap {:left [\Q \W \F \P \B
\A \R \S \T \G
\Z \X \C \D \V]

:left-thumbs ["Tab" "Space"]

:right ["J" "L" "U" "Y" "Del"
"M" "N" "E" "I" "O"
"K" "H" "," "." "/"]
:right [\J \L \U \Y "Del"
\M \N \E \I \O
\K \H \, \. \/]

:right-thumbs ["Bspc" "Esc"]}}]}
:right-thumbs ["Bspc" "Esc"]}}

{:name "Lower"
:keymap {:left [1 2 3 4 5
6 7 8 9 0
"C-z" "C-x" "C-c" "C-v" "C-s"]

:left-thumbs [nil nil]

:right ["PgUp" "Home" "Up" "End" nil
"PgDn" "Left" "Down" "Right" nil
nil \Æ \Ø \Å nil]

:right-thumbs ["Enter" "Caps Word"]}}

{:name "Raise"
:keymap {:left [\! \@ \{ \} \%
\^ \$ \[ \] \#
\\ \| \` \~ \?]

:left-thumbs [nil nil]

:right [\& \< \> \" \=
\* \( \) \' \+
\/ \_ \; \: \-]

:right-thumbs [nil "RAlt"]}}

{:name "Adjust"
:keymap {:left ["Reset" nil "Bri -" "Bri +" "Toggle RBG"
nil "Mute" "Vol Down" "Vol Up" "Toggle Audio"
nil nil nil nil nil]

:left-thumbs [nil nil]

:right ["UC WinC" "Caps Lock" "Scroll Lock" "Num Lock" "Insert"
"UC Mac" "Back" "Forward" "WWW Home" "WWW Refresh"
"UC Linux" "Prev" "Play" "Stop" "Next"]

:right-thumbs [nil nil]}}

{:name "Fn"
:keymap {:left ["F1" "F2" "F3" "F4" "F5"
"F6" "F7" "F8" "F9" "F10"
"F11" "F12" "Menu" "Print Screen" "Caps Word"]

:left-thumbs [nil nil]

:right ["," "7" "8" "9" "."
"*" "4" "5" "6" "+"
"/" "1" "2" "3" "-"]

:right-thumbs ["Enter" "0"]}}

{:name "Accents"
:keymap {:left ["à" "ä" "â" "ã" "á"
"è" "ë" "ê" "ẽ" "é"
"ì" "ï" "î" "ĩ" "í"]

:left-thumbs ["Symbols" "Emoji"]

:right ["ò" "ö" "ô" "õ" "ó"
"ù" "ü" "û" "ũ" "ú"
"ỳ" "ÿ" "ŷ" "ỹ" "ý"]

:right-thumbs [nil nil]}}

{:name "Symbols"
:keymap {:left ["½" "²" "∫" "°" "•"
"∞" "®" "√" "™" "∇"
"█" "▒" "©" "∂" "∪"]

:left-thumbs ["∈" "Exit Layer"]

:right ["€" "£" "↑" "¥" "≠"
"×" "←" "↓" "→" "±"
"∩" "¬" "∅" "–" "—"]

:right-thumbs ["✓" "✗"]}}

{:name "Emoji"
:keymap {:left ["😏" "😳" "😩" "😉" "🙄"
"😅" "😍" "😭" "😭" "😘"
"😂" "🤪" "😔" "😜" "😒"]

:left-thumbs ["🅱" "Exit Layer"]

:right ["🤔" "🤫" "👌" "👀" "✨"
"🙃" "😬" "👏" "🧠" "💦"
"💩" "😤" "🙏" "🔥" "💕"]

:right-thumbs ["✅" "⛔"]}}]}
4 changes: 2 additions & 2 deletions examples/svg/split_3x5_2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 14 additions & 2 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,17 @@
:test-paths ["test"]

:main ^:skip-aot keymap-viz.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
:target-path "target/%s/"

:profiles
{:uberjar {:aot :all
:omit-source true
:uberjar-name "keymap-viz.jar"}

:dev [:project/dev]

:project/dev {:source-paths ["env/dev/src"
"env/test/src"]
:resource-paths ["env/dev/resources"
"env/test/resources"]
:repl-options {:init-ns user}}})
8 changes: 5 additions & 3 deletions src/keymap_viz/config.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
[mount.core :as m]))

(def ^:private default-config
{:style :default
{:border-radius 6
:grid-size-horz 60
:grid-size-vert 50
:key-padding 3
:border-radius 6})
:layer-margin 30
:style :default})

(m/defstate default-style
:start (->> (io/resource "styles/default.css")
Expand All @@ -29,4 +30,5 @@
(eval)))

(m/defstate config
:start (get keymap :config default-config))
:start (merge default-config
(:config keymap)))
5 changes: 3 additions & 2 deletions src/keymap_viz/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
[hiccup.core :as h]
[keymap-viz.config :as conf]
[keymap-viz.rendering :as r]
[mount.core :as m])
[mount.core :as m]
[keymap-viz.utils :as u])
(:gen-class))

(defn -main
Expand All @@ -13,6 +14,6 @@
(m/start)

(->> (r/render-keymap conf/keymap conf/layout)
(apply r/render-svg)
(apply r/render-svg (u/calc-keymap-size conf/keymap conf/layout))
(h/html)
(println)))
57 changes: 32 additions & 25 deletions src/keymap_viz/rendering.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,39 @@
(:require
[hiccup.core :as h]
[keymap-viz.config :as conf]
[keymap-viz.utils :refer [vector-sum]]))
[keymap-viz.utils :as u]))

;; HELPER FUNCTIONS ------------------------------------------------------------
(defmulti keymap-key->keymap-opts class)
(defmethod keymap-key->keymap-opts String [k] {:tap k})
(defmethod keymap-key->keymap-opts clojure.lang.PersistentArrayMap [m] m)
(defmulti key-labels->key-opts class)
(defmethod key-labels->key-opts nil [_] {:tap ""})
(defmethod key-labels->key-opts Number [n] {:tap n})
(defmethod key-labels->key-opts Character [c] {:tap c})
(defmethod key-labels->key-opts String [s] {:tap s})
(defmethod key-labels->key-opts clojure.lang.PersistentArrayMap [m] m)

(defn make-key
"Construct a key map with all the data necessary for rendering"
[keymap-key layout-opts block-pos]
(let [{:keys [type pos size]} layout-opts
keymap-opts (keymap-key->keymap-opts keymap-key)
keymap-opts (key-labels->key-opts keymap-key)
{:keys [tap hold]} keymap-opts
key-pos (vector-sum pos block-pos)]
{:type type :size size :pos key-pos :tap tap :hold hold}))
key-pos (u/vector-sum pos block-pos)]
{:type type :size size :pos key-pos :tap (str tap) :hold (str hold)}))

;; RENDERING FUNCTIONS ---------------------------------------------------------
(defmulti render-key
"Translate a key config map into Hiccup data."
:type)
(fn [key _]
(:type key)))

(defmethod render-key :rect render-key-rect [key]
(defmethod render-key :rect render-key-rect [key yshift]
;; TODO: add support for multiword texts and Hold texts
(let [{:keys [key-padding border-radius grid-size-horz grid-size-vert]} conf/config
(let [{:keys [key-padding border-radius grid-size-horz grid-size-vert layer-margin]} conf/config
{:keys [size pos tap hold]} key
rect-x (+ key-padding (* grid-size-horz (first pos)))
rect-y (+ key-padding (* grid-size-vert (second pos)))
text-x (* grid-size-horz (+ (first pos) (* 0.5 size)))
text-y (* grid-size-vert (+ (second pos) 0.5))]
rect-x (+ layer-margin key-padding (* grid-size-horz (first pos)))
rect-y (+ layer-margin key-padding (* grid-size-vert (second pos)) yshift)
text-x (+ layer-margin (* grid-size-horz (+ (first pos) (* 0.5 size))))
text-y (+ layer-margin (* grid-size-vert (+ (second pos) 0.5)) yshift)]
[[:rect {:x rect-x :y rect-y
:rx border-radius :ry border-radius
:width (- (* size grid-size-horz) (* 2 key-padding))
Expand All @@ -39,39 +43,42 @@

(defn render-block
"Translate a keymap block into a vector of Hiccup elements."
[keymap-block layout-block]
[keymap-block layout-block yshift]
(let [block-pos (:pos layout-block)
layout-keys (:keys layout-block)]
(->> (map make-key keymap-block layout-keys (repeat block-pos))
(map render-key)
layout-keys (:keys layout-block)
keys (map make-key keymap-block layout-keys (repeat block-pos))]
(->> (map render-key keys (repeat yshift))
(apply concat)
(into []))))

(defn render-layer
"Translate a single keymap layers into a vector of rendered blocks."
[keymap-layer layout]
[keymap-layer layout yshift]
(let [keymap (:keymap keymap-layer)
block-layouts (:blocks layout)
block-keys (keys block-layouts)]
(->> (for [k block-keys]
(render-block (k keymap) (k block-layouts)))
(render-block (k keymap) (k block-layouts) yshift))
(apply concat)
(into []))))

(defn render-keymap
"Translate a keymap definition into Hiccup data."
[keymap layout]
(let [keymap-layers (:layers keymap)]
(->> (map render-layer keymap-layers (repeat layout))
(let [keymap-layers (:layers keymap)
[_ yshift] (u/calc-board-size layout)]
(->> (iterate (partial + yshift) 0)
(map render-layer keymap-layers (repeat layout))
(apply concat)
(into []))))

(defn render-svg
"Create an SVG element as Hiccup data."
[& elements]
[size & elements]
;; TODO: make hardcoded values configurable/inferred from config
(let [w (* 11 (:grid-size-horz conf/config))
h (* 4 (:grid-size-vert conf/config))
(let [{:keys [layer-margin grid-size-horz grid-size-vert]} conf/config
w (first size)
h (second size)
viewbox-str (str "0 0 " w " " h)
xmlns "http://www.w3.org/2000/svg"]
(-> [:svg {:width w :height h :viewBox viewbox-str :xmlns xmlns} [:style conf/default-style]]
Expand Down
30 changes: 29 additions & 1 deletion src/keymap_viz/utils.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
(ns keymap-viz.utils)
(ns keymap-viz.utils
(:require
[keymap-viz.config :as conf]))

(defn vector-sum
"Returns the sum of one or more equal-sized numeric vectors."
Expand All @@ -8,3 +10,29 @@
(vec (map + x y)))
([x y & more]
(reduce vector-sum (concat [x y] more))))

(defmulti calc-key-corner (fn [key _] (:type key)))

(defmethod calc-key-corner :rect [key block-pos]
(let [{:keys [layer-margin grid-size-horz grid-size-vert]} conf/config
pos (vector-sum block-pos (:pos key))
size (:size key)
x (+ layer-margin (* grid-size-horz (+ size (first pos))))
y (+ layer-margin (* grid-size-vert (inc (second pos))))]
[x y]))

(defn calc-board-size [layout]
(let [{:keys [layer-margin]} conf/config
key-corners (->> (:blocks layout)
vals
(map (fn [{:keys [keys pos]}]
(map calc-key-corner keys (repeat pos))))
(apply concat))
xmax (first (apply max-key first key-corners))
ymax (second (apply max-key second key-corners))]
(mapv #(+ layer-margin %) [xmax ymax])))

(defn calc-keymap-size [keymap layout]
(let [n-layers (count (:layers keymap))
[x y] (calc-board-size layout)]
[x (* y n-layers)]))
Loading