Added biggest-to-the-middle-sort
This commit is contained in:
parent
e392369373
commit
3ae27a4012
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -3,3 +3,11 @@
|
||||||
/*-init.clj
|
/*-init.clj
|
||||||
/resources/public/js/compiled
|
/resources/public/js/compiled
|
||||||
out
|
out
|
||||||
|
|
||||||
|
\.lein-repl-history
|
||||||
|
|
||||||
|
\.nrepl-port
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
*.zip
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# swingometer
|
# swingometer
|
||||||
|
|
||||||
A [re-frame](https://github.com/Day8/re-frame) application designed to ... well, that part is up to you.
|
A [re-frame](https://github.com/Day8/re-frame) application designed to show votes in an election.
|
||||||
|
|
||||||
## Development Mode
|
## Development Mode
|
||||||
|
|
||||||
|
|
1
resources/public/swingometer
Symbolic link
1
resources/public/swingometer
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
swingometer
|
|
@ -61,13 +61,11 @@
|
||||||
:validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}])
|
:validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}])
|
||||||
|
|
||||||
|
|
||||||
(def log (.-log js/console))
|
;; (defn abs
|
||||||
|
;; "Return the absolute value of the (numeric) argument."
|
||||||
|
;; [n] (max n (- n)))
|
||||||
|
|
||||||
|
|
||||||
(defn abs
|
|
||||||
"Return the absolute value of the (numeric) argument."
|
|
||||||
[n] (max n (- n)))
|
|
||||||
|
|
||||||
;; the constant 140 represents the full sweep of the needle
|
;; the constant 140 represents the full sweep of the needle
|
||||||
;; from the left end of the scale to right end, in degrees.
|
;; from the left end of the scale to right end, in degrees.
|
||||||
(def full-scale-deflection 140)
|
(def full-scale-deflection 140)
|
||||||
|
@ -112,7 +110,7 @@
|
||||||
[arg]
|
[arg]
|
||||||
(if
|
(if
|
||||||
(and (number? arg) (not (integer? arg)))
|
(and (number? arg) (not (integer? arg)))
|
||||||
(.toFixed arg 2)
|
(.toFixed arg 1)
|
||||||
arg))
|
arg))
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,27 +134,51 @@
|
||||||
:x cx
|
:x cx
|
||||||
:y (- cy min-radius)} (as-label label)]])
|
:y (- cy min-radius)} (as-label label)]])
|
||||||
|
|
||||||
|
(def model {:snp {:id :snp :name "Scottish National Party" :colour "yellow" :votes 100}
|
||||||
|
:lab {:id :lab :name "Labour Party" :colour "red" :votes 90}
|
||||||
|
:con {:id :con :name "Conservative Party" :colour "blue" :votes 80}
|
||||||
|
:ld {:id :ld :name "Liberal Democrats" :colour "GoldenRod" :votes 70}
|
||||||
|
:grn {:id :grn :name "Scottish Green Party" :colour "green" :votes 60}
|
||||||
|
:ukp {:id :ukp :name "United Kingdom Independence Party" :colour "DarkViolet" :votes 50}})
|
||||||
|
|
||||||
|
(defn biggest-to-the-middle-sort
|
||||||
|
"Sort this list of `maps` representing parties so that those with the most votes are in
|
||||||
|
the middle."
|
||||||
|
[maps]
|
||||||
|
(let [first-sort (sort-by :votes maps)
|
||||||
|
evens (take-nth 2 first-sort)
|
||||||
|
odds (take-nth 2 (rest first-sort))]
|
||||||
|
(concat evens (reverse odds))))
|
||||||
|
|
||||||
|
|
||||||
(defn recursively-draw-segments
|
(defn recursively-draw-segments
|
||||||
|
"Walk down a list of parties, returning a labelled SVG arc segment for each one.
|
||||||
|
`still-to-do` is the (remainder of the) list of parties being scanned, should
|
||||||
|
initially be the whole list;
|
||||||
|
`done` is the parties which have been scanned, and should initially be `nil`.
|
||||||
|
`total-votes` is the total number of votes for all parties.
|
||||||
|
`cx` and `cy` are the cartesian coordinates of the centre of arc.
|
||||||
|
`radius` is the radius of the arc."
|
||||||
[still-to-do done total-votes cx cy radius]
|
[still-to-do done total-votes cx cy radius]
|
||||||
(log (string/join " " ["\nstill-to-do" still-to-do "\ndone" done "\ntotal-votes" total-votes "cx" cx "cy" cy "radius" radius]))
|
|
||||||
(if
|
(if
|
||||||
(empty? still-to-do) nil
|
(empty? still-to-do) nil
|
||||||
(let [votes-done (reduce + (map :votes done))
|
(let [votes-done (reduce + (map :votes done))
|
||||||
start-angle (deflection votes-done 0 total-votes)
|
start-angle (deflection votes-done 0 total-votes)
|
||||||
party (first still-to-do)
|
party (first still-to-do)
|
||||||
end-angle (deflection (+ (:votes party) votes-done) 0 total-votes)]
|
end-angle (deflection (+ (:votes party) votes-done) 0 total-votes)
|
||||||
|
others (recursively-draw-segments (rest still-to-do) (cons party done) total-votes cx cy radius)
|
||||||
|
vote-share (* (/ (:votes party) total-votes) 100)]
|
||||||
|
(if (> vote-share 1)
|
||||||
(cons [:g [:path {:class "snm-scale"
|
(cons [:g [:path {:class "snm-scale"
|
||||||
:id (str (:id party) "-segment")
|
:id (str (:id party) "-segment")
|
||||||
:style {:stroke (:colour party)}
|
:style {:stroke (:colour party)}
|
||||||
:d (describe-arc cx cy radius start-angle end-angle)}]
|
:d (describe-arc cx cy radius start-angle end-angle)}]
|
||||||
(gradation cx cy (* radius 0.8) (* radius 1.1) start-angle
|
(gradation cx cy (* radius 0.8) (* radius 1.1) start-angle
|
||||||
(let [vote-share (* (/ (:votes party) total-votes) 100)]
|
|
||||||
(str
|
(str
|
||||||
(if (> vote-share 5) (name (:id party)) "")
|
(if (> vote-share 5) (name (:id party)) "")
|
||||||
(if (> vote-share 10) (str " " (int vote-share) "%")))))]
|
(if (> vote-share 10) (str " " (as-label vote-share) "%"))))]
|
||||||
(recursively-draw-segments (rest still-to-do) (cons party done) total-votes cx cy radius)))))
|
others)
|
||||||
|
others))))
|
||||||
|
|
||||||
|
|
||||||
(defn swingometer
|
(defn swingometer
|
||||||
|
@ -209,23 +231,8 @@
|
||||||
:d (describe-arc cx cy scale-radius
|
:d (describe-arc cx cy scale-radius
|
||||||
(- 0 mid-point-deflection)
|
(- 0 mid-point-deflection)
|
||||||
mid-point-deflection)}]
|
mid-point-deflection)}]
|
||||||
;; (if (and (> gradations 0) (> total-votes 0))
|
|
||||||
;; (apply vector (cons :g (map #(let
|
|
||||||
;; [value (*
|
|
||||||
;; (/
|
|
||||||
;; total-votes
|
|
||||||
;; gradations) %)]
|
|
||||||
;; (gradation cx cy gradation-inner needle-length
|
|
||||||
;; (deflection value 0 total-votes)
|
|
||||||
;; value))
|
|
||||||
;; (range 0 (+ gradations 1))))))
|
|
||||||
(apply vector
|
(apply vector
|
||||||
(cons :g (recursively-draw-segments (map model (sort (keys model))) nil total-votes cx cy scale-radius)))
|
(cons :g (recursively-draw-segments (biggest-to-the-middle-sort (vals model)) nil total-votes cx cy scale-radius)))
|
||||||
[:rect {:class frame-class
|
[:rect {:class frame-class
|
||||||
:id (str id "-frame")
|
:id (str id "-frame")
|
||||||
:x (* width 0.05) :y (* height .05) :height cy :width (* width 0.9)}]
|
:x (* width 0.05) :y (* height .05) :height cy :width (* width 0.9)}]]]]))
|
||||||
;; [:circle {:class hub-class
|
|
||||||
;; :id (str id "-hub")
|
|
||||||
;; :r (/ height 10) :cx cx :cy cy}]
|
|
||||||
]
|
|
||||||
]]))
|
|
||||||
|
|
Loading…
Reference in a new issue