diff --git a/.gitignore b/.gitignore index 94ac55c..e560088 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ out *.zip .lsp/ .clj-kondo/ + +*.svg diff --git a/resources/public/sample-data.edn b/resources/public/sample-data.edn index 51d121e..98516b8 100644 --- a/resources/public/sample-data.edn +++ b/resources/public/sample-data.edn @@ -10,8 +10,13 @@ :label "Labour" :colour "red" :quantity 9712011 - :children [{:id "negative" - :colour "gray" + :children [{:id "other" + :label "Other" + :colour "#C0C0C0" + :quantity 291140} + {:id "negative" + :label "Negative" + :colour "#C0C0C0" :children [{:id "anti-tory" :label "To get the Tories out" :colour "#424242" @@ -35,13 +40,11 @@ :label "To oust the SNP" :colour "#424242" :quantity 97047}]} - {:id "other" - :colour "maroon" - :quantity 291140} {:id "positive" + :label "Positive" :colour "red" :children [{:id "policies" - :label "I agree with their policies" + :label "Agree with policies" :colour "#d32f2f" :quantity 485233} {:id "mp" @@ -63,11 +66,11 @@ :colour "#d32f2f" :quantity 97047} {:id "fwc" - :label "For the working class" + :label "For working class" :colour "#d32f2f" :quantity 97047} {:id "cl" - :label "Address the cost of living" + :label "Address cost of living" :colour "#d32f2f" :quantity 97047} {:id "stability" @@ -79,7 +82,7 @@ :colour "#d32f2f" :quantity 97047} {:id "services" - :label "For better public services" + :label "Better public services" :colour "#d32f2f" :quantity 97047}]}]} {:id "con" @@ -98,7 +101,7 @@ :label "Sinn Féin" :quantity 210891} {:id "independents" - :label "independents" + :label "Independents" :colour "silver" :quantity 564243} {:id "reform" @@ -106,10 +109,10 @@ :colour "cyan" :quantity 4091549} {:id "dup" - :label "Democratic Unionist Party" + :label "DUP" :quantity 172058} {:id "greenew" - :label "Green Party of England and Wales" + :label "Green Party" :colour "green" :quantity 1939502} {:id "pc" @@ -121,5 +124,5 @@ :colour "#f6cb2f" :quantity 117191} {:id "sdlp" - :label "Social Democratic and Labour Party" + :label "SDLP" :quantity 86861}]}]} \ No newline at end of file diff --git a/src/clj/rsvggraph/core.clj b/src/clj/rsvggraph/core.clj index 27be4f6..c0c5689 100644 --- a/src/clj/rsvggraph/core.clj +++ b/src/clj/rsvggraph/core.clj @@ -1,12 +1,15 @@ (ns rsvggraph.core - (:require [clojure.math :refer [cos PI sin]] + (:require [clojure.math :refer [cos floor PI sin]] [clojure.string :refer [join replace]] [clojure.xml :refer [emit]] - [fastmath.core :refer [pow]] [hiccup2.core :refer [html]] [rsvggraph.data :refer [normalise]])) +(def ^:dynamic *background* "white") + +(def ^:dynamic *foreground* "black") + (defn polar-to-cartesian "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)] (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 [datum diameter] (let [r' (/ diameter 2) @@ -38,7 +56,8 @@ start-angle (* (:left datum) 360) end-angle (* (:right datum) 360) 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)) [:g {:id (str id "group")} [:path {:class "rsvggraph-segment" @@ -47,39 +66,51 @@ :stroke (:colour datum) :stroke-width thickness} :d path-data}] - [:text [:textPath {:href (str "#" id) - :path path-data} (:label datum)]]])) + (text-path datum tp-id diameter thickness start-angle end-angle) + [: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)]]]])) + +;; Did not vote (defn flatten-data [data] (cond (empty? (:children data)) data :else (flatten (cons (dissoc data :children) (map flatten-data (:children data)))))) -(def ^:dynamic *background* "white") - -(def ^:dynamic *foreground* "black") - (defn data->svg [data diameter] - (let [data' (normalise data)] + (let [data' (normalise data) + dimension (* 2 diameter)] [:svg {:xmlSpace "preserve" :overflow "visible" - :viewBox (join " " [0 0 diameter diameter]) - :width (str diameter "px") - :height (str diameter "px") + :viewBox (join " " [0 0 dimension dimension]) + :width (str dimension "px") + :height (str dimension "px") :y "0px" :x "0px" :version "1.1" :id (:id data') :class (str "rsvggraph-graph") - :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 "http://www.w3.org/2000/svg" + :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-anchor "middle" - :x (/ diameter 2) - :y (/ diameter 2) + :x diameter + :y diameter :width (/ diameter 4) :id (str (:id data') "-title") + :style {:font-family "sans-serif" + :font-weight "bold"} :class "rsvggraph-value"} [:tspan (:label data) ": " (:quantity data)]] (map #(draw-segment % diameter) (flatten-data data))]))