By Jove! I think he's got it!

This commit is contained in:
Simon Brooke 2024-07-09 23:21:26 +01:00
parent 240b76c889
commit 2b6388de9d
3 changed files with 66 additions and 30 deletions

2
.gitignore vendored
View file

@ -13,3 +13,5 @@ out
*.zip *.zip
.lsp/ .lsp/
.clj-kondo/ .clj-kondo/
*.svg

View file

@ -10,8 +10,13 @@
:label "Labour" :label "Labour"
:colour "red" :colour "red"
:quantity 9712011 :quantity 9712011
:children [{:id "negative" :children [{:id "other"
:colour "gray" :label "Other"
:colour "#C0C0C0"
:quantity 291140}
{:id "negative"
:label "Negative"
:colour "#C0C0C0"
:children [{:id "anti-tory" :children [{:id "anti-tory"
:label "To get the Tories out" :label "To get the Tories out"
:colour "#424242" :colour "#424242"
@ -35,13 +40,11 @@
:label "To oust the SNP" :label "To oust the SNP"
:colour "#424242" :colour "#424242"
:quantity 97047}]} :quantity 97047}]}
{:id "other"
:colour "maroon"
:quantity 291140}
{:id "positive" {:id "positive"
:label "Positive"
:colour "red" :colour "red"
:children [{:id "policies" :children [{:id "policies"
:label "I agree with their policies" :label "Agree with policies"
:colour "#d32f2f" :colour "#d32f2f"
:quantity 485233} :quantity 485233}
{:id "mp" {:id "mp"
@ -63,11 +66,11 @@
:colour "#d32f2f" :colour "#d32f2f"
:quantity 97047} :quantity 97047}
{:id "fwc" {:id "fwc"
:label "For the working class" :label "For working class"
:colour "#d32f2f" :colour "#d32f2f"
:quantity 97047} :quantity 97047}
{:id "cl" {:id "cl"
:label "Address the cost of living" :label "Address cost of living"
:colour "#d32f2f" :colour "#d32f2f"
:quantity 97047} :quantity 97047}
{:id "stability" {:id "stability"
@ -79,7 +82,7 @@
:colour "#d32f2f" :colour "#d32f2f"
:quantity 97047} :quantity 97047}
{:id "services" {:id "services"
:label "For better public services" :label "Better public services"
:colour "#d32f2f" :colour "#d32f2f"
:quantity 97047}]}]} :quantity 97047}]}]}
{:id "con" {:id "con"
@ -98,7 +101,7 @@
:label "Sinn Féin" :label "Sinn Féin"
:quantity 210891} :quantity 210891}
{:id "independents" {:id "independents"
:label "independents" :label "Independents"
:colour "silver" :colour "silver"
:quantity 564243} :quantity 564243}
{:id "reform" {:id "reform"
@ -106,10 +109,10 @@
:colour "cyan" :colour "cyan"
:quantity 4091549} :quantity 4091549}
{:id "dup" {:id "dup"
:label "Democratic Unionist Party" :label "DUP"
:quantity 172058} :quantity 172058}
{:id "greenew" {:id "greenew"
:label "Green Party of England and Wales" :label "Green Party"
:colour "green" :colour "green"
:quantity 1939502} :quantity 1939502}
{:id "pc" {:id "pc"
@ -121,5 +124,5 @@
:colour "#f6cb2f" :colour "#f6cb2f"
:quantity 117191} :quantity 117191}
{:id "sdlp" {:id "sdlp"
:label "Social Democratic and Labour Party" :label "SDLP"
:quantity 86861}]}]} :quantity 86861}]}]}

View file

@ -1,12 +1,15 @@
(ns rsvggraph.core (ns rsvggraph.core
(:require [clojure.math :refer [cos PI sin]] (:require [clojure.math :refer [cos floor PI sin]]
[clojure.string :refer [join replace]] [clojure.string :refer [join replace]]
[clojure.xml :refer [emit]] [clojure.xml :refer [emit]]
[fastmath.core :refer [pow]]
[hiccup2.core :refer [html]] [hiccup2.core :refer [html]]
[rsvggraph.data :refer [normalise]])) [rsvggraph.data :refer [normalise]]))
(def ^:dynamic *background* "white")
(def ^:dynamic *foreground* "black")
(defn polar-to-cartesian (defn polar-to-cartesian
"Return, as a map with keys :x. :y, the cartesian coordinates at the point "Return, as a map with keys :x. :y, the cartesian coordinates at the point
@ -30,6 +33,21 @@
sweep (if (> end-angle start-angle) 1 0)] sweep (if (> end-angle start-angle) 1 0)]
(join " " ["M" (:x start) (:y start) "A" radius radius 0 large-arc? sweep (:x end) (:y end)]))) (join " " ["M" (:x start) (:y start) "A" radius radius 0 large-arc? sweep (:x end) (:y end)])))
(defn- text-path [datum tp-id diameter thickness start-angle end-angle]
[:path {:class "rsvggraph-text-path"
:id tp-id
:style {:fill "none"
:stroke "none"}
:d (let [angle (/ (+ start-angle end-angle) 2)
radius (/ diameter 2)
start (polar-to-cartesian diameter diameter radius angle)
end (polar-to-cartesian diameter diameter diameter angle)]
(if (< (- (:right datum) (:left datum)) 0.08)
(format "M %d %d L %d %d" (int (:x start)) (int (:y start))
(int (:x end)) (int (:y end)))
(describe-arc diameter diameter (- radius (* 0.9 thickness)) start-angle end-angle)))}])
(defn draw-segment (defn draw-segment
[datum diameter] [datum diameter]
(let [r' (/ diameter 2) (let [r' (/ diameter 2)
@ -38,7 +56,8 @@
start-angle (* (:left datum) 360) start-angle (* (:left datum) 360)
end-angle (* (:right datum) 360) end-angle (* (:right datum) 360)
id (str (:id datum) "-segment") id (str (:id datum) "-segment")
path-data (describe-arc r' r' radius start-angle end-angle)] tp-id (str "tp-" id)
path-data (describe-arc diameter diameter radius start-angle end-angle)]
;; (println (format "Id: %s; radius: %s; start: %s; end: %s; thickness %s" id radius start-angle end-angle thickness)) ;; (println (format "Id: %s; radius: %s; start: %s; end: %s; thickness %s" id radius start-angle end-angle thickness))
[:g {:id (str id "group")} [:g {:id (str id "group")}
[:path {:class "rsvggraph-segment" [:path {:class "rsvggraph-segment"
@ -47,39 +66,51 @@
:stroke (:colour datum) :stroke (:colour datum)
:stroke-width thickness} :stroke-width thickness}
:d path-data}] :d path-data}]
[:text [:textPath {:href (str "#" id) (text-path datum tp-id diameter thickness start-angle end-angle)
:path path-data} (:label datum)]]])) [:text {:style {:fill *foreground*
:font-family "sans-serif"
:font-weight "bold"
:font-size (str (floor (* 0.2 thickness)))}}
[:textPath {:xlink:href (str "#" tp-id)} [:tspan (:label datum)]]]]))
;; <text
;; xml:space="preserve"
;; style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffcc00;stroke-width:4.9852"
;; id="text562"><textPath
;; xlink:href="#no-show-segment"
;; id="textPath770"><tspan
;; id="tspan560">Did not vote</tspan></textPath></text>
(defn flatten-data (defn flatten-data
[data] [data]
(cond (empty? (:children data)) data (cond (empty? (:children data)) data
:else (flatten (cons (dissoc data :children) (map flatten-data (:children data)))))) :else (flatten (cons (dissoc data :children) (map flatten-data (:children data))))))
(def ^:dynamic *background* "white")
(def ^:dynamic *foreground* "black")
(defn data->svg (defn data->svg
[data diameter] [data diameter]
(let [data' (normalise data)] (let [data' (normalise data)
dimension (* 2 diameter)]
[:svg {:xmlSpace "preserve" [:svg {:xmlSpace "preserve"
:overflow "visible" :overflow "visible"
:viewBox (join " " [0 0 diameter diameter]) :viewBox (join " " [0 0 dimension dimension])
:width (str diameter "px") :width (str dimension "px")
:height (str diameter "px") :height (str dimension "px")
:y "0px" :y "0px"
:x "0px" :x "0px"
:version "1.1" :version "1.1"
:id (:id data') :id (:id data')
:class (str "rsvggraph-graph") :class (str "rsvggraph-graph")
:xmlns "http://www.w3.org/2000/svg"} :xmlns "http://www.w3.org/2000/svg"
[:circle {:id (str (:id data') "-background") :cx (/ diameter 2) :cy (/ diameter 2) :r (/ diameter 2) :style {:fill "white"}}] :xmlns:xlink "http://www.w3.org/1999/xlink"}
[:circle {:id (str (:id data') "-background") :cx diameter :cy diameter :r (/ diameter 2) :style {:fill "white"}}]
[:text [:text
{:text-anchor "middle" {:text-anchor "middle"
:x (/ diameter 2) :x diameter
:y (/ diameter 2) :y diameter
:width (/ diameter 4) :width (/ diameter 4)
:id (str (:id data') "-title") :id (str (:id data') "-title")
:style {:font-family "sans-serif"
:font-weight "bold"}
:class "rsvggraph-value"} [:tspan (:label data) ": " (:quantity data)]] :class "rsvggraph-value"} [:tspan (:label data) ": " (:quantity data)]]
(map #(draw-segment % diameter) (flatten-data data))])) (map #(draw-segment % diameter) (flatten-data data))]))