From 0c4c52d6f424f7c12f1cb3967bd3ca885949536b Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Sun, 9 Jul 2017 16:01:58 +0100 Subject: [PATCH 1/7] Very initial start of a pen trace meter Nothing even begins to work yet. --- .../pen_trace_meter.cljs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/cljs/swinging_needle_meter/pen_trace_meter.cljs diff --git a/src/cljs/swinging_needle_meter/pen_trace_meter.cljs b/src/cljs/swinging_needle_meter/pen_trace_meter.cljs new file mode 100644 index 0000000..661ec6f --- /dev/null +++ b/src/cljs/swinging_needle_meter/pen_trace_meter.cljs @@ -0,0 +1,43 @@ +(ns swinging-needle-meter.pen-trace-meter + (:require [re-com.core :refer [h-box v-box box gap line label title slider checkbox p]] + [re-com.box :refer [flex-child-style]] + [re-com.util :refer [deref-or-value]] + [re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]] + [reagent.core :as reagent])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; pen-trace-meter: an experiment in animating SVG from re-frame. +;;;; Draws heavily on re-com.. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2014 Simon Brooke +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; ------------------------------------------------------------------------------------ +;; Component: pen-trace-meter +;; ------------------------------------------------------------------------------------ + +(defn pen-trace-meter + [& {:keys [model width height min-value max-value] + :or {width "100%" + height "100%" + min-value 0 + max-value 100 + }}] + ) From 6dca39ce91749e1d8a39477de1404870fb89b6fd Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 11 Jul 2017 16:46:35 +0100 Subject: [PATCH 2/7] All angles now depend on the deflection function This is preparatory to making the deflection function pluggable. --- .../swinging_needle_meter.cljs | 76 ++++++++++--------- src/cljs/swinging_needle_meter/views.cljs | 1 + 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index 54beb1b..30482ca 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -82,6 +82,7 @@ {:name :attr :required false :type "HTML attr map" :validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}]) + (defn abs "Return the absolute value of the (numeric) argument." [n] (max n (- n))) @@ -90,21 +91,22 @@ ;; from the left end of the scale to right end, in degrees. (def full-scale-deflection 140) + ;; ultimately this should be resizeable, and radius should be a function of ;; size... (def scale-radius 75) + (defn deflection - "Return the deflection of a needle given this `value` on the + "Return the linear deflection of a needle given this `value` on the range `min-value`...`max-value`." [value min-value max-value] (let [range (- max-value min-value) - deflection (/ value range) zero-offset (/ (- 0 min-value) range) - limited (min (max (+ zero-offset deflection) 0) 1)] - (js/console.log (str "zero-offset: " zero-offset)) + limited (min (max (+ zero-offset (/ value range)) 0) 1)] (* (- limited 0.5) full-scale-deflection))) + (defn polar-to-cartesian "Return, as a map with keys :x. :y, the cartesian coordinates at the point `radius` distance at `theta` (degrees) angle from a point at @@ -115,6 +117,7 @@ {:x (+ cx (* radius (.cos js/Math in-radians))) :y (+ cy (* radius (.sin js/Math in-radians)))})) + (defn describe-arc "Return as a string an SVG path definition describing an arc centred at `cx`, cy` starting at `start-angle` and ending at `end-angle` (both @@ -141,18 +144,21 @@ "Return as a string an SVG path definition describing a radial stroke from a center at `cx`, cy` starting at `min-radius` and extending to `max-radius`." [cx cy min-radius max-radius angle label] - (let - [start (polar-to-cartesian cx cy min-radius angle) - mid (polar-to-cartesian cx cy (+ min-radius - (* (- max-radius min-radius) 0.333)) - angle) - end (polar-to-cartesian cx cy max-radius angle)] - [:g {:class "snm-gradation"} - [:path {:d (string/join " " ["M" (:x mid) (:y mid) "L" (:x end) (:y end)])}] - [:text {:text-anchor "middle" - :x (:x start) - :y (:y start) - :transform (string/join " " ["rotate(" angle (:x start) (:y start) ")"])} (as-label label)]])) + [:g {:class "snm-gradation" + :transform (string/join " " ["rotate(" angle cx cy ")"])} + [:path {:d (string/join + " " + ["M" + cx + (- cy + (+ min-radius + (* (- max-radius min-radius) 0.333))) + "L" + cx + (- cy max-radius)])}] + [:text {:text-anchor "middle" + :x cx + :y (- cy min-radius)} (as-label label)]]) (defn swinging-needle-meter @@ -178,12 +184,7 @@ {:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]} (let [model (deref-or-value model) setpoint (deref-or-value setpoint) - mid-point-deflection (/ full-scale-deflection 2) - ;; if warn-value is greater than max-value, we don't want a red-zone at all. - red-zone-deflection (if - (< warn-value max-value) - (* full-scale-deflection (/ warn-value max-value)) - full-scale-deflection)] + mid-point-deflection (/ full-scale-deflection 2)] [box :align :start :child [:div @@ -197,9 +198,7 @@ {:width width :height height} style)} attr) - [:svg {:xmlns:svg "http://www.w3.org/2000/svg" - :xmlns "http://www.w3.org/2000/svg" - :xml:space "preserve" + [:svg {:xmlSpace "preserve" :overflow "visible" :viewBox "0 0 180 120" :y "0px" @@ -216,11 +215,14 @@ :class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]] [:path {:class scale-class :id (str id "-scale") - :d (describe-arc 80 100 scale-radius (- 0 mid-point-deflection) mid-point-deflection)}] + :d (describe-arc 80 100 scale-radius + (deflection min-value min-value max-value) + (deflection max-value min-value max-value))}] [:path {:class redzone-class :id (str id "-redzone") - :d (describe-arc 80 100 scale-radius (- red-zone-deflection mid-point-deflection) mid-point-deflection)}] - + :d (describe-arc 80 100 scale-radius + (deflection warn-value min-value max-value) + (deflection max-value min-value max-value))}] [:path {:class cursor-class :id (str id "-cursor") :d "M 80,20 80,100" @@ -230,15 +232,15 @@ :id (str id "-needle") :d "M 80,20 80,100" :transform (str "rotate( " (deflection model min-value max-value) ", 80, 100)") }] - (apply vector (cons :g (map #(gradation 80 100 60 82 - (- (* % - (/ full-scale-deflection gradations)) - mid-point-deflection) - (+ min-value - (* - (/ - (- max-value min-value) - gradations) %))) + (apply vector (cons :g (map #(let + [value (+ min-value + (* + (/ + (- max-value min-value) + gradations) %))] + (gradation 80 100 60 82 + (deflection value min-value max-value) + value)) (range 0 (+ gradations 1))))) [:rect {:class frame-class :id (str id "-frame") diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index 729d191..167dc69 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -63,6 +63,7 @@ ;; :max-value (aget js/Math "PI") :tolerance 2 :alarm-class "snm-warning" + :gradations 5 :width "350px"] [title :level :level3 :label "Parameters"] [h-box From 6f611ec122bcd28cea2ad6a328d876e5b9077ae2 Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 11 Jul 2017 17:51:17 +0100 Subject: [PATCH 3/7] Entirely resizeable --- .../public/css/swinging-needle-meter.css | 16 +++--- .../swinging_needle_meter.cljs | 49 ++++++++++--------- src/cljs/swinging_needle_meter/views.cljs | 3 +- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/resources/public/css/swinging-needle-meter.css b/resources/public/css/swinging-needle-meter.css index 26f1909..b7664f0 100644 --- a/resources/public/css/swinging-needle-meter.css +++ b/resources/public/css/swinging-needle-meter.css @@ -8,13 +8,13 @@ .snm-cursor { stroke:#ff8500; - stroke-width:5; + stroke-width: 3%; stroke-opacity: 0.5; } .snm-frame { fill: none; - stroke-width: 10; + stroke-width: 5%; stroke-linejoin: round; stroke: #444444; } @@ -25,7 +25,7 @@ } .snm-gradation text { - font-size: 50%; + font-size: 150%; font-weight: lighter; } @@ -33,11 +33,6 @@ fill: #444444; } -.snm-limit { - text-align: center; - font-size: 66%; -} - .snm-meter { height: 50%; width: auto; @@ -51,13 +46,13 @@ .snm-redzone { fill:none; stroke: maroon; - stroke-width: 15; + stroke-width: 10%; } .snm-scale { fill: none; stroke: green; - stroke-width: 15; + stroke-width: 10%; } .snm-target .snm-frame { @@ -65,6 +60,7 @@ } .snm-value { + font-size: 200%; text-align: center; } diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index 30482ca..dd8e98b 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -41,10 +41,10 @@ :validate-fn number-or-string? :description "current value of the variable being watched. A number between 0 and 100"} {:name :setpoint :required false :type "double | atom" :validate-fn number-or-string? :description "current setpoint for the variable being watched, if any. A number between 0 and 100"} - {:name :width :required false :type "string" :default "100%" - :validate-fn string? :description "a CSS width"} - {:name :height :required false :type "string" :default "100%" - :validate-fn string? :description "a CSS height"} + {:name :width :required false :type "integer" :default "300" + :validate-fn integer? :description "a CSS width"} + {:name :height :required false :type "integer" :default "200" + :validate-fn integer? :description "a CSS height"} {:name :min-value :required false :type "double" :default 0 :validate-fn number? :description "the minimum value model can take"} {:name :max-value :required false :type "double" :default 100 @@ -92,11 +92,6 @@ (def full-scale-deflection 140) -;; ultimately this should be resizeable, and radius should be a function of -;; size... -(def scale-radius 75) - - (defn deflection "Return the linear deflection of a needle given this `value` on the range `min-value`...`max-value`." @@ -164,8 +159,8 @@ (defn swinging-needle-meter "Render an SVG swinging needle meter" [& {:keys [model setpoint width height min-value max-value warn-value tolerance class gradations alarm-class cursor-class frame-class hub-class needle-class redzone-class scale-class target-class unit id style attr] - :or {width "100%" - height "100%" + :or {width 300 + height 200 min-value 0 max-value 100 warn-value 80 @@ -184,7 +179,12 @@ {:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]} (let [model (deref-or-value model) setpoint (deref-or-value setpoint) - mid-point-deflection (/ full-scale-deflection 2)] + mid-point-deflection (/ full-scale-deflection 2) + cx (/ width 2) + cy (* height 0.90) + needle-length (* height 0.75) + scale-radius (* height 0.7) + gradation-inner (* height 0.55)] [box :align :start :child [:div @@ -200,52 +200,53 @@ attr) [:svg {:xmlSpace "preserve" :overflow "visible" - :viewBox "0 0 180 120" + :viewBox (string/join " " [0 0 width height]) :y "0px" :x "0px" :version "1.1" :id id :class (str "snm-meter " class)} + [:text {:text-anchor "middle" - :x 80 - :y 70 + :x (/ width 2) + :y (/ height 2) :width "100" :id (str id "-current-value") :class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]] [:path {:class scale-class :id (str id "-scale") - :d (describe-arc 80 100 scale-radius + :d (describe-arc cx cy scale-radius (deflection min-value min-value max-value) (deflection max-value min-value max-value))}] [:path {:class redzone-class :id (str id "-redzone") - :d (describe-arc 80 100 scale-radius + :d (describe-arc cx cy scale-radius (deflection warn-value min-value max-value) (deflection max-value min-value max-value))}] [:path {:class cursor-class :id (str id "-cursor") - :d "M 80,20 80,100" + :d (str "M " cx "," (- cy needle-length) " " cx "," cy) ;; "M cx,20 cx,100" :visibility (if (and (number? setpoint) (> setpoint min-value)) "visible" "hidden") - :transform (str "rotate( " (deflection setpoint min-value max-value) ", 80, 100)")}] + :transform (str "rotate( " (deflection setpoint min-value max-value) "," cx "," cy ")")}] [:path {:class needle-class :id (str id "-needle") - :d "M 80,20 80,100" - :transform (str "rotate( " (deflection model min-value max-value) ", 80, 100)") }] + :d (str "M " cx "," (- cy needle-length) " " cx "," cy) ;; "M cx,20 cx,100" + :transform (str "rotate( " (deflection model min-value max-value) "," cx "," cy ")") }] (apply vector (cons :g (map #(let [value (+ min-value (* (/ (- max-value min-value) gradations) %))] - (gradation 80 100 60 82 + (gradation cx cy gradation-inner needle-length (deflection value min-value max-value) value)) (range 0 (+ gradations 1))))) [:rect {:class frame-class :id (str id "-frame") - :x "5" :y "5" :height "100" :width "150"}] + :x (* width 0.05) :y (* height .05) :height cy :width (* width 0.9)}] [:circle {:class hub-class :id (str id "-hub") - :r "10" :cx "80" :cy "100"}]] + :r (/ height 10) :cx cx :cy cy}]] ]])) diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index 167dc69..f877727 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -64,7 +64,8 @@ :tolerance 2 :alarm-class "snm-warning" :gradations 5 - :width "350px"] + :height 300 + :width 500] [title :level :level3 :label "Parameters"] [h-box :gap "10px" From 3ebc2612fa8cd31086d32327f9805dfa5fee49a9 Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 11 Jul 2017 21:44:16 +0100 Subject: [PATCH 4/7] Lots of prettiness. --- resources/public/css/chosen-sprite.png | Bin 0 -> 646 bytes .../swinging_needle_meter.cljs | 29 ++++-- src/cljs/swinging_needle_meter/views.cljs | 93 +++++++++++++++--- 3 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 resources/public/css/chosen-sprite.png diff --git a/resources/public/css/chosen-sprite.png b/resources/public/css/chosen-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..3611ae4ace1c4b1cbeacd6145b5a79cbc72e0bdc GIT binary patch literal 646 zcmV;10(t$3P)<h;3K|Lk000e1NJLTq001-q001Qj1^@s64f4v>0006_Nkl<Zc-rmO zziSh57=ZCp!No$b5DZk@+)pBbK$lj?l1Z>&pbAC0n?)s%2x5M$#UgGxI1~gymp~v; zh<`zGaTJP5BybQY4tlRo;SIcmE0t>ueW3>*u6N;@_u;;|BoL8PuhZ#FWY9$(fl<H! zS#Lpo2qF_z)MG*4ThJ;FOw@*n86tEQq?R#;AlCf|Ap&<nY7?=Es!U!p7aS|a8z(wx zQ1OU6l%Ge?!i<A#OoVkrHp*V`8j|3B%AhR1;@S*@dM2iz=kgjO7}yIIu@5mu$fXQ= z2SIO!K^gQWF-C}8<fbnuLyUVVm)sqQ@!%vurBdmi*+Hh4-iMT*p+yWKMueq#2ibx4 zu#_@r2a=#WgP;t$$WMh7FhDlN(`l}c?dhOC1d)j<>an1oVvxDBL8~}0Q5z;^2p>Ov z7}Q$E7=l>$BZLUt1*uKMCaQwKYf$lsJCyerWMd+%BeGH6f_f&Vpy%=$BN%uK%Ahxi zF+%Jj=Pc-WXF;djS<ruDw`Q|>iJw|m4>cN%^Fi|FBZ!E{_yJLS?RI<3U696XLkQSs z{{&kQJ$K(#5CgC1;GbA>mjm56zJZ^-Hg2_ASKI_K@CybPh7Rq}8ud``)NM~eZx}qT z)oK?Lf>t!B;%Uh}*P+)Ef?jbx#w|BNEVb(l{2LK}m#fw4xx>ddF;&j}!S{N-e!<&Q z?k<D~VgCi+AjX!fAYEQJ@s{B~>vzyI_AT@H-u4tUz1h&GCh8>?TnA~?BZ7WGP<9`r gQ6DuZO!_~60ju@{TzXF%TmS$707*qoM6N<$g2#CqL;wH) literal 0 HcmV?d00001 diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index dd8e98b..6152f90 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -156,6 +156,12 @@ :y (- cy min-radius)} (as-label label)]]) +(defn as-mm + "return the argument, as a string, with 'mm' appended" + [arg] + (str arg "mm")) + + (defn swinging-needle-meter "Render an SVG swinging needle meter" [& {:keys [model setpoint width height min-value max-value warn-value tolerance class gradations alarm-class cursor-class frame-class hub-class needle-class redzone-class scale-class target-class unit id style attr] @@ -201,6 +207,8 @@ [:svg {:xmlSpace "preserve" :overflow "visible" :viewBox (string/join " " [0 0 width height]) + :width (str width "px") + :height (str height "px") :y "0px" :x "0px" :version "1.1" @@ -233,16 +241,17 @@ :id (str id "-needle") :d (str "M " cx "," (- cy needle-length) " " cx "," cy) ;; "M cx,20 cx,100" :transform (str "rotate( " (deflection model min-value max-value) "," cx "," cy ")") }] - (apply vector (cons :g (map #(let - [value (+ min-value - (* - (/ - (- max-value min-value) - gradations) %))] - (gradation cx cy gradation-inner needle-length - (deflection value min-value max-value) - value)) - (range 0 (+ gradations 1))))) + (if (> gradations 0) + (apply vector (cons :g (map #(let + [value (+ min-value + (* + (/ + (- max-value min-value) + gradations) %))] + (gradation cx cy gradation-inner needle-length + (deflection value min-value max-value) + value)) + (range 0 (+ gradations 1)))))) [:rect {:class frame-class :id (str id "-frame") :x (* width 0.05) :y (* height .05) :height cy :width (* width 0.9)}] diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index f877727..a4cb1b5 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -1,6 +1,7 @@ (ns swinging-needle-meter.views (:require [re-frame.core :as re-frame] - [re-com.core :refer [h-box v-box box gap line label title progress-bar slider checkbox p]] + [re-com.core :refer [h-box v-box box gap line label title progress-bar slider checkbox p single-dropdown]] + [re-com.util :refer [deref-or-value]] [swinging-needle-meter.swinging-needle-meter :refer [swinging-needle-meter swinging-needle-args-desc]] [swinging-needle-meter.utils :refer [panel-title title2 args-table github-hyperlink status-text]] [reagent.core :as reagent])) @@ -11,8 +12,14 @@ (defn swinging-needle-demo [] - (let [value (reagent/atom 75) - setpoint (reagent/atom 75)] + (let [value (reagent/atom 60) + setpoint (reagent/atom 75) + gradations (reagent/atom 5) + size (reagent/atom 70) + min-val (reagent/atom 0) + max-val (reagent/atom 100) + warn-val (reagent/atom 80) + unit (reagent/atom "Mw")] (fn [] [v-box @@ -56,23 +63,22 @@ :children [[swinging-needle-meter :model value :setpoint setpoint - :unit "Mw" -;; :min-value 20 -;; :warn-value 35 -;; :max-value 40 -;; :max-value (aget js/Math "PI") + :unit (deref-or-value unit) + :min-value (deref-or-value min-val) + :warn-value (deref-or-value warn-val) + :max-value (deref-or-value max-val) :tolerance 2 :alarm-class "snm-warning" - :gradations 5 - :height 300 - :width 500] + :gradations (deref-or-value gradations) + :height (int (* (deref-or-value size) 6)) + :width (int (* (deref-or-value size) 10))] [title :level :level3 :label "Parameters"] [h-box :gap "10px" :children [[box :align :start :child [:code ":model"]] [slider :model value - :min 0 + :min -100 :max 100 :width "200px" :on-change #(reset! value %)] @@ -82,11 +88,72 @@ :children [[box :align :start :child [:code ":setpoint"]] [slider :model setpoint - :min 0 + :min -100 :max 100 :width "200px" :on-change #(reset! setpoint %)] [label :label @setpoint]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":min-val"]] + [slider + :model min-val + :min -100 + :max 100 + :width "200px" + :on-change #(reset! min-val %)] + [label :label @min-val]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":max-val"]] + [slider + :model max-val + :min -100 + :max 100 + :width "200px" + :on-change #(reset! max-val %)] + [label :label @max-val]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":warn-val"]] + [slider + :model warn-val + :min -100 + :max 100 + :width "200px" + :on-change #(reset! warn-val %)] + [label :label @warn-val]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":gradations"]] + [slider + :model gradations + :min 0 + :max 10 + :width "200px" + :on-change #(reset! gradations %)] + [label :label @gradations]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":unit"]] + [single-dropdown + :model unit + :choices [{:id "Mw" :label "Megawatts" :group "Electrical"} + {:id "M/s" :label "Metres per second" :group "Motion"} + {:id "F/f" :label "Furlongs per fortnight" :group "Motion"} + {:id "°C" :label "Degrees Celsius" :group "Temperature"}] + :width "200px" + :on-change #(reset! unit %)]]] + [h-box + :gap "10px" + :children [[box :align :start :child [:code ":size"]] + [slider + :model size + :min 25 + :max 100 + :width "200px" + :on-change #(reset! size %)] + [label :label @size]]] ]]]]]]]]))) From b3c624eb5c56369f9fb8cd207eb94caa44755d68 Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 18 Jul 2017 13:33:52 +0100 Subject: [PATCH 5/7] Now working correctly with publish/subscribe model --- project.clj | 13 +-- resources/public/index.html | 1 + src/cljs/swinging_needle_meter/db.cljs | 4 - src/cljs/swinging_needle_meter/events.cljs | 55 +++++++++++- .../pen_trace_meter.cljs | 43 ---------- src/cljs/swinging_needle_meter/state.cljs | 14 ++++ src/cljs/swinging_needle_meter/subs.cljs | 48 ++++++++++- .../swinging_needle_meter.cljs | 8 +- src/cljs/swinging_needle_meter/utils.cljs | 8 +- src/cljs/swinging_needle_meter/views.cljs | 84 ++++++++++--------- 10 files changed, 171 insertions(+), 107 deletions(-) delete mode 100644 src/cljs/swinging_needle_meter/db.cljs delete mode 100644 src/cljs/swinging_needle_meter/pen_trace_meter.cljs create mode 100644 src/cljs/swinging_needle_meter/state.cljs diff --git a/project.clj b/project.clj index 2e37dc6..dd955a5 100644 --- a/project.clj +++ b/project.clj @@ -5,7 +5,8 @@ [reagent "0.6.0"] [re-frame "0.9.4"] [org.clojure/core.async "0.2.391"] - [re-com "2.0.0"]] + [re-com "2.0.0"] + [org.webjars.bower/snap.svg "0.4.1"]] :plugins [[lein-cljsbuild "1.1.4"]] @@ -20,9 +21,7 @@ :profiles {:dev {:dependencies [[binaryage/devtools "0.8.2"]] - - :plugins [[lein-figwheel "0.5.9"]] - }} + :plugins [[lein-figwheel "0.5.9"]]}} :cljsbuild {:builds @@ -44,8 +43,4 @@ :output-to "resources/public/js/compiled/app.js" :optimizations :advanced :closure-defines {goog.DEBUG false} - :pretty-print false}} - - - ]} - ) + :pretty-print false}}]}) diff --git a/resources/public/index.html b/resources/public/index.html index 91a5ada..f6f63a4 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -14,6 +14,7 @@ </head> <body> <div id="app"></div> + <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script> <script src="js/compiled/app.js"></script> <script>swinging_needle_meter.core.init();</script> </body> diff --git a/src/cljs/swinging_needle_meter/db.cljs b/src/cljs/swinging_needle_meter/db.cljs deleted file mode 100644 index a71f237..0000000 --- a/src/cljs/swinging_needle_meter/db.cljs +++ /dev/null @@ -1,4 +0,0 @@ -(ns swinging-needle-meter.db) - -(def default-db - {:name "re-frame"}) diff --git a/src/cljs/swinging_needle_meter/events.cljs b/src/cljs/swinging_needle_meter/events.cljs index d1183e6..b474e3f 100644 --- a/src/cljs/swinging_needle_meter/events.cljs +++ b/src/cljs/swinging_needle_meter/events.cljs @@ -1,10 +1,57 @@ (ns swinging-needle-meter.events (:require [re-frame.core :as re-frame] - [swinging-needle-meter.db :as db])) - -;;; This file is unchanged (except this line) from the leiningen recom template + [swinging-needle-meter.state :as state])) +;; Reset. (re-frame/reg-event-db :initialize-db (fn [_ _] - db/default-db)) + state/default-state)) + +;; The clock ticked. +(re-frame/reg-event-db + :timer + (fn [db _] + db)) + +(re-frame/reg-event-db + :set-value + (fn [db [x value]] + (js/console.log (str :set-value " " x " " value)) + (assoc db :value value))) + +(re-frame/reg-event-db + :set-setpoint + (fn [db [_ value]] + (assoc db :setpoint value))) + +(re-frame/reg-event-db + :set-gradations + (fn [db [_ value]] + (assoc db :gradations value))) + +(re-frame/reg-event-db + :set-size + (fn [db [_ value]] + (assoc db :size value))) + +(re-frame/reg-event-db + :set-min-value + (fn [db [_ value]] + (assoc db :min-val value))) + +(re-frame/reg-event-db + :set-max-value + (fn [db [_ value]] + (assoc db :max-val value))) + +(re-frame/reg-event-db + :set-warning-value + (fn [db [_ value]] + (assoc db :warn-val value))) + +(re-frame/reg-event-db + :set-unit + (fn [db [_ value]] + (assoc db :unit value))) + diff --git a/src/cljs/swinging_needle_meter/pen_trace_meter.cljs b/src/cljs/swinging_needle_meter/pen_trace_meter.cljs deleted file mode 100644 index 661ec6f..0000000 --- a/src/cljs/swinging_needle_meter/pen_trace_meter.cljs +++ /dev/null @@ -1,43 +0,0 @@ -(ns swinging-needle-meter.pen-trace-meter - (:require [re-com.core :refer [h-box v-box box gap line label title slider checkbox p]] - [re-com.box :refer [flex-child-style]] - [re-com.util :refer [deref-or-value]] - [re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]] - [reagent.core :as reagent])) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; -;;;; pen-trace-meter: an experiment in animating SVG from re-frame. -;;;; Draws heavily on re-com.. -;;;; -;;;; This program is free software; you can redistribute it and/or -;;;; modify it under the terms of the GNU General Public License -;;;; as published by the Free Software Foundation; either version 2 -;;;; of the License, or (at your option) any later version. -;;;; -;;;; This program is distributed in the hope that it will be useful, -;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;;;; GNU General Public License for more details. -;;;; -;;;; You should have received a copy of the GNU General Public License -;;;; along with this program; if not, write to the Free Software -;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -;;;; USA. -;;;; -;;;; Copyright (C) 2014 Simon Brooke -;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; ------------------------------------------------------------------------------------ -;; Component: pen-trace-meter -;; ------------------------------------------------------------------------------------ - -(defn pen-trace-meter - [& {:keys [model width height min-value max-value] - :or {width "100%" - height "100%" - min-value 0 - max-value 100 - }}] - ) diff --git a/src/cljs/swinging_needle_meter/state.cljs b/src/cljs/swinging_needle_meter/state.cljs new file mode 100644 index 0000000..0ea97f2 --- /dev/null +++ b/src/cljs/swinging_needle_meter/state.cljs @@ -0,0 +1,14 @@ +(ns ^{:doc "Client state." + :author "Simon Brooke"} + swinging-needle-meter.state) + +(def default-state + {:timer (js/Date.) + :value 60 + :setpoint 75 + :gradations 5 + :size 70 + :min-val 0 + :max-val 100 + :warn-val 80 + :unit "Mw"}) diff --git a/src/cljs/swinging_needle_meter/subs.cljs b/src/cljs/swinging_needle_meter/subs.cljs index f1844c7..0e488c4 100644 --- a/src/cljs/swinging_needle_meter/subs.cljs +++ b/src/cljs/swinging_needle_meter/subs.cljs @@ -2,9 +2,53 @@ (:require-macros [reagent.ratom :refer [reaction]]) (:require [re-frame.core :as re-frame])) -;;; This file is unchanged (except this line) from the leiningen recom template - (re-frame/reg-sub :name (fn [db] (:name db))) + +(re-frame/reg-sub + :value + (fn [db] + (:value db))) + +(re-frame/reg-sub + :setpoint + (fn [db] + (:setpoint db))) + +(re-frame/reg-sub + :unit + (fn [db] + (:unit db))) + +(re-frame/reg-sub + :min-val + (fn [db] + (:min-val db))) + +(re-frame/reg-sub + :max-val + (fn [db] + (:max-val db))) + +(re-frame/reg-sub + :gradations + (fn [db] + (:gradations db))) + +(re-frame/reg-sub + :warn-val + (fn [db] + (:warn-val db))) + +(re-frame/reg-sub + :size + (fn [db] + (:size db))) + +(re-frame/reg-sub + :timer + (fn [db] + (:timer db))) + diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index 6152f90..3044805 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -4,7 +4,8 @@ [re-com.box :refer [flex-child-style]] [re-com.util :refer [deref-or-value]] [re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [swinging-needle-meter.utils :refer [abs]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -82,11 +83,6 @@ {:name :attr :required false :type "HTML attr map" :validate-fn html-attr? :description [:span "HTML attributes, like " [:code ":on-mouse-move"] [:br] "No " [:code ":class"] " or " [:code ":style"] "allowed"]}]) - -(defn abs - "Return the absolute value of the (numeric) argument." - [n] (max n (- n))) - ;; the constant 140 represents the full sweep of the needle ;; from the left end of the scale to right end, in degrees. (def full-scale-deflection 140) diff --git a/src/cljs/swinging_needle_meter/utils.cljs b/src/cljs/swinging_needle_meter/utils.cljs index b85df97..9b5de77 100644 --- a/src/cljs/swinging_needle_meter/utils.cljs +++ b/src/cljs/swinging_needle_meter/utils.cljs @@ -1,9 +1,15 @@ (ns swinging-needle-meter.utils (:require [re-com.core :refer [h-box v-box box gap title line label hyperlink-href align-style]])) -;;;; This file is just stolen wholesale from re-demo in the re-com package; +;;;; This file is mostly stolen wholesale from re-demo in the re-com package; ;;;; I claim no credit for it. + +(defn abs + "Return the absolute value of the (numeric) argument." + [n] (max n (- n))) + + (defn github-hyperlink "given a label and a relative path, return a component which hyperlinks to the GitHub URL in a new tab" [label src-path] diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index a4cb1b5..8fac950 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -1,25 +1,33 @@ (ns swinging-needle-meter.views - (:require [re-frame.core :as re-frame] + (:require [re-frame.core :as rf] [re-com.core :refer [h-box v-box box gap line label title progress-bar slider checkbox p single-dropdown]] [re-com.util :refer [deref-or-value]] [swinging-needle-meter.swinging-needle-meter :refer [swinging-needle-meter swinging-needle-args-desc]] [swinging-needle-meter.utils :refer [panel-title title2 args-table github-hyperlink status-text]] - [reagent.core :as reagent])) + [reagent.core :as reagent] + [swinging-needle-meter.utils :refer [abs]])) ;; ------------------------------------------------------------------------------------ ;; Demo: swinging-needle-meter ;; ------------------------------------------------------------------------------------ +(defn dispatch-timer-event + [] + (let [now (js/Date.)] + (rf/dispatch [:timer now]))) ;; <-- dispatch used + +;; call the dispatching function every tenth of a second +(defonce do-timer (js/setInterval dispatch-timer-event 100)) + + (defn swinging-needle-demo [] - (let [value (reagent/atom 60) - setpoint (reagent/atom 75) - gradations (reagent/atom 5) - size (reagent/atom 70) - min-val (reagent/atom 0) - max-val (reagent/atom 100) - warn-val (reagent/atom 80) - unit (reagent/atom "Mw")] + (let [unit @(rf/subscribe [:unit]) + min-val @(rf/subscribe [:min-val]) + max-val @(rf/subscribe [:max-val]) + warn-val @(rf/subscribe [:warn-val]) + gradations @(rf/subscribe [:gradations]) + size @(rf/subscribe [:size])] (fn [] [v-box @@ -61,48 +69,48 @@ [v-box :gap "20px" :children [[swinging-needle-meter - :model value - :setpoint setpoint - :unit (deref-or-value unit) - :min-value (deref-or-value min-val) - :warn-value (deref-or-value warn-val) - :max-value (deref-or-value max-val) + :model @(rf/subscribe [:value]) + :setpoint @(rf/subscribe [:setpoint]) + :unit @(rf/subscribe [:unit]) + :min-value @(rf/subscribe [:min-val]) + :warn-value @(rf/subscribe [:warn-val]) + :max-value @(rf/subscribe [:max-val]) :tolerance 2 :alarm-class "snm-warning" - :gradations (deref-or-value gradations) - :height (int (* (deref-or-value size) 6)) - :width (int (* (deref-or-value size) 10))] + :gradations @(rf/subscribe [:gradations]) + :height (int (* @(rf/subscribe [:size]) 6)) + :width (int (* @(rf/subscribe [:size]) 10))] [title :level :level3 :label "Parameters"] [h-box :gap "10px" :children [[box :align :start :child [:code ":model"]] [slider - :model value + :model @(rf/subscribe [:value]) :min -100 :max 100 :width "200px" - :on-change #(reset! value %)] - [label :label @value]]] + :on-change #(rf/dispatch [:set-value %])] + [label :label @(rf/subscribe [:value])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":setpoint"]] [slider - :model setpoint + :model @(rf/subscribe [:setpoint]) :min -100 :max 100 :width "200px" - :on-change #(reset! setpoint %)] - [label :label @setpoint]]] + :on-change #(rf/dispatch [:set-setpoint %])] + [label :label @(rf/subscribe [:setpoint])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":min-val"]] [slider - :model min-val + :model @(rf/subscribe [:min-val]) :min -100 :max 100 :width "200px" - :on-change #(reset! min-val %)] - [label :label @min-val]]] + :on-change #(rf/dispatch [:set-min-value %])] + [label :label @(rf/subscribe [:min-val])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":max-val"]] @@ -111,8 +119,8 @@ :min -100 :max 100 :width "200px" - :on-change #(reset! max-val %)] - [label :label @max-val]]] + :on-change #(rf/dispatch [:set-max-value %])] + [label :label max-val]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":warn-val"]] @@ -121,8 +129,8 @@ :min -100 :max 100 :width "200px" - :on-change #(reset! warn-val %)] - [label :label @warn-val]]] + :on-change #(rf/dispatch [:set-warning-value %])] + [label :label warn-val]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":gradations"]] @@ -131,19 +139,19 @@ :min 0 :max 10 :width "200px" - :on-change #(reset! gradations %)] - [label :label @gradations]]] + :on-change #(rf/dispatch [:set-gradations %])] + [label :label gradations]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":unit"]] [single-dropdown - :model unit + :model @(rf/subscribe [:unit]) :choices [{:id "Mw" :label "Megawatts" :group "Electrical"} {:id "M/s" :label "Metres per second" :group "Motion"} {:id "F/f" :label "Furlongs per fortnight" :group "Motion"} {:id "°C" :label "Degrees Celsius" :group "Temperature"}] :width "200px" - :on-change #(reset! unit %)]]] + :on-change #(rf/dispatch [:set-unit %])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":size"]] @@ -152,8 +160,8 @@ :min 25 :max 100 :width "200px" - :on-change #(reset! size %)] - [label :label @size]]] + :on-change #(rf/dispatch [:set-size %])] + [label :label size]]] ]]]]]]]]))) From c83b557d418e81fffd2b8861cf15648273aa9f7c Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 18 Jul 2017 13:49:32 +0100 Subject: [PATCH 6/7] And now with inertia --- src/cljs/swinging_needle_meter/core.cljs | 14 +++++++++++--- src/cljs/swinging_needle_meter/events.cljs | 15 ++++++++++----- src/cljs/swinging_needle_meter/state.cljs | 1 + src/cljs/swinging_needle_meter/subs.cljs | 5 +++++ src/cljs/swinging_needle_meter/views.cljs | 10 +--------- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/cljs/swinging_needle_meter/core.cljs b/src/cljs/swinging_needle_meter/core.cljs index ea96433..04ceafc 100644 --- a/src/cljs/swinging_needle_meter/core.cljs +++ b/src/cljs/swinging_needle_meter/core.cljs @@ -1,6 +1,6 @@ (ns swinging-needle-meter.core (:require [reagent.core :as reagent] - [re-frame.core :as re-frame] + [re-frame.core :as rf] [swinging-needle-meter.events] [swinging-needle-meter.subs] [swinging-needle-meter.views :as views] @@ -14,12 +14,20 @@ (enable-console-print!) (println "dev mode"))) +(defn dispatch-timer-event + [] + (let [now (js/Date.)] + (rf/dispatch [:timer now]))) ;; <-- dispatch used + +;; call the dispatching function every tenth of a second +(defonce do-timer (js/setInterval dispatch-timer-event 100)) + (defn mount-root [] - (re-frame/clear-subscription-cache!) + (rf/clear-subscription-cache!) (reagent/render [views/main-panel] (.getElementById js/document "app"))) (defn ^:export init [] - (re-frame/dispatch-sync [:initialize-db]) + (rf/dispatch-sync [:initialize-db]) (dev-setup) (mount-root)) diff --git a/src/cljs/swinging_needle_meter/events.cljs b/src/cljs/swinging_needle_meter/events.cljs index b474e3f..b35980c 100644 --- a/src/cljs/swinging_needle_meter/events.cljs +++ b/src/cljs/swinging_needle_meter/events.cljs @@ -8,17 +8,22 @@ (fn [_ _] state/default-state)) -;; The clock ticked. +;; The clock ticked. Implement a mechanical swing. (re-frame/reg-event-db :timer - (fn [db _] - db)) + (fn [db [x value]] + (let [old-value (:old-value db) + target (:value db) + new-value (+ old-value (/ (- target old-value) 10))] + (assoc db :old-value new-value)))) (re-frame/reg-event-db :set-value (fn [db [x value]] - (js/console.log (str :set-value " " x " " value)) - (assoc db :value value))) + (assoc + (assoc db :old-value (:value db)) + :value + value))) (re-frame/reg-event-db :set-setpoint diff --git a/src/cljs/swinging_needle_meter/state.cljs b/src/cljs/swinging_needle_meter/state.cljs index 0ea97f2..b69bed9 100644 --- a/src/cljs/swinging_needle_meter/state.cljs +++ b/src/cljs/swinging_needle_meter/state.cljs @@ -5,6 +5,7 @@ (def default-state {:timer (js/Date.) :value 60 + :old-value 0 :setpoint 75 :gradations 5 :size 70 diff --git a/src/cljs/swinging_needle_meter/subs.cljs b/src/cljs/swinging_needle_meter/subs.cljs index 0e488c4..8414bdf 100644 --- a/src/cljs/swinging_needle_meter/subs.cljs +++ b/src/cljs/swinging_needle_meter/subs.cljs @@ -12,6 +12,11 @@ (fn [db] (:value db))) +(re-frame/reg-sub + :old-value + (fn [db] + (:old-value db))) + (re-frame/reg-sub :setpoint (fn [db] diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index 8fac950..688eaa9 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -11,14 +11,6 @@ ;; Demo: swinging-needle-meter ;; ------------------------------------------------------------------------------------ -(defn dispatch-timer-event - [] - (let [now (js/Date.)] - (rf/dispatch [:timer now]))) ;; <-- dispatch used - -;; call the dispatching function every tenth of a second -(defonce do-timer (js/setInterval dispatch-timer-event 100)) - (defn swinging-needle-demo [] @@ -69,7 +61,7 @@ [v-box :gap "20px" :children [[swinging-needle-meter - :model @(rf/subscribe [:value]) + :model @(rf/subscribe [:old-value]) :setpoint @(rf/subscribe [:setpoint]) :unit @(rf/subscribe [:unit]) :min-value @(rf/subscribe [:min-val]) From ef4653e3b25377dcb70a560e8539dea165170ff0 Mon Sep 17 00:00:00 2001 From: simon <simon@journeyman.cc> Date: Tue, 18 Jul 2017 13:56:41 +0100 Subject: [PATCH 7/7] Minor fix. --- project.clj | 2 +- src/cljs/swinging_needle_meter/views.cljs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/project.clj b/project.clj index dd955a5..4a37b04 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject swinging-needle-meter "1.0.0" +(defproject swinging-needle-meter "1.0.1" :description "A swinging needle meter, as an experiment in animating SVG from re-frame. Draws heavily on re-com." :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/clojurescript "1.9.229"] diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index 688eaa9..d89bd40 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -107,32 +107,32 @@ :gap "10px" :children [[box :align :start :child [:code ":max-val"]] [slider - :model max-val + :model @(rf/subscribe [:max-val]) :min -100 :max 100 :width "200px" :on-change #(rf/dispatch [:set-max-value %])] - [label :label max-val]]] + [label :label @(rf/subscribe [:max-val])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":warn-val"]] [slider - :model warn-val + :model @(rf/subscribe [:warn-val]) :min -100 :max 100 :width "200px" :on-change #(rf/dispatch [:set-warning-value %])] - [label :label warn-val]]] + [label :label @(rf/subscribe [:warn-val])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":gradations"]] [slider - :model gradations + :model @(rf/subscribe [:gradations]) :min 0 :max 10 :width "200px" :on-change #(rf/dispatch [:set-gradations %])] - [label :label gradations]]] + [label :label @(rf/subscribe [:gradations])]]] [h-box :gap "10px" :children [[box :align :start :child [:code ":unit"]]