From 315e320fcdad490a500de1ca172702c55a5a5e5c Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 9 Jul 2017 16:00:28 +0100 Subject: [PATCH] Pretty much done There are things which could be improved, but then there always are... --- README.md | 10 ++ .../public/css/swinging-needle-meter.css | 21 ++- resources/public/images/example.png | Bin 13253 -> 12555 bytes resources/public/images/example.xcf | Bin 51297 -> 31331 bytes .../swinging_needle_meter.cljs | 140 +++++++++++++----- src/cljs/swinging_needle_meter/views.cljs | 34 +++-- 6 files changed, 155 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 2bb426b..befdbc6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,16 @@ Works well in Chrome and Firefox; not tested in Internet Exploder. ![what it should look like](resources/public/images/example.png) +## Intended uses + +This is a component for a console, typically one controlling a technical or scientific instrument. It is +by design flexible and configurable. Minimum value, maximum value, warning value and number of gradations +shown are all configurable, as are (obviously) styles. + +A cursor will be shown if the value of *setpoint* is between *min-value* and *max-value*; *setpoint* is a dynamic value which is watched during the run. While the *model* value is within +- *tolerance* of the *setpoint* value, a class *target-class* is set on the meter indicating an on-target status (by default the frame goes green). + +A red-zone may be shown if a *warn-value* is set which is between *min-value* and *max-value*. If such a *warn-value* is set, then if the current value (*model*) exceeds *warn-value*, a class *warning-class* is set on the meter indicating a warning status (by default the frame goes maroon). + ## Development Mode ### Run application: diff --git a/resources/public/css/swinging-needle-meter.css b/resources/public/css/swinging-needle-meter.css index 8dc6ad4..26f1909 100644 --- a/resources/public/css/swinging-needle-meter.css +++ b/resources/public/css/swinging-needle-meter.css @@ -19,6 +19,16 @@ stroke: #444444; } +.snm-gradation path { + stroke: black; + stroke-width: 1; +} + +.snm-gradation text { + font-size: 50%; + font-weight: lighter; +} + .snm-hub { fill: #444444; } @@ -50,13 +60,14 @@ stroke-width: 15; } +.snm-target .snm-frame { + stroke: green; +} + .snm-value { text-align: center; } -.snm-warning { - fill: none; - stroke-width: 10; - stroke-linejoin: round; - stroke: red; +.snm-warning .snm-frame { + stroke: maroon; } diff --git a/resources/public/images/example.png b/resources/public/images/example.png index d46afaddffee24247fb36874d057752667031f2e..a4e54d6584cdf1ffd976707d4336e8252feb9bcf 100644 GIT binary patch literal 12555 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 literal 13253 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 MG7#yaS8oIV7ul6}c>n+a diff --git a/resources/public/images/example.xcf b/resources/public/images/example.xcf index b4f10b56dd1149db52155783f40875d36de2b2a0..845079a6a1db2becefc3d924717369fcef591a9d 100644 GIT binary patch literal 31331 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 E09NIldjJ3c literal 51297 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 diff --git a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs index 6d661c6..54beb1b 100644 --- a/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs +++ b/src/cljs/swinging_needle_meter/swinging_needle_meter.cljs @@ -1,5 +1,6 @@ (ns swinging-needle-meter.swinging-needle-meter - (:require [re-com.core :refer [h-box v-box box gap line label title slider checkbox p]] + (:require [clojure.string :as string] + [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]] @@ -30,7 +31,7 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ------------------------------------------------------------------------------------ -;; Component: swinging-needle +;; Component: swinging-needle-meter ;; ------------------------------------------------------------------------------------ ;;; It seems the defaults given here are just documentation; the defaults @@ -48,10 +49,14 @@ :validate-fn number? :description "the minimum value model can take"} {:name :max-value :required false :type "double" :default 100 :validate-fn number? :description "the maximum value model can take"} + {:name :warn-value :required false :type "double" :default 80 + :validate-fn number? :description "the maximum safe value model can take"} + {:name :tolerance :required false :type "double" :default 3 + :validate-fn number? :description "the amount by which model can differ from setpoint and still be considered acceptable"} {:name :class :required false :type "string" :validate-fn string? :description "CSS class names, space separated, for the top-level SVG element"} - {:name :alarm-class :required false :type "string" - :validate-fn string? :description "CSS class names, space separated, for the cursor"} + {:name :alarm-class :required false :type "string" :default "snm-warning" + :validate-fn string? :description "CSS class names, space separated, applied to the frame in an alarm condition"} {:name :cursor-class :required false :type "string" :default "snm-cursor" :validate-fn string? :description "CSS class names, space separated, for the cursor"} {:name :frame-class :required false :type "string" :default "snm-frame" @@ -64,10 +69,14 @@ :validate-fn string? :description "CSS class names, space separated, for the scale"} {:name :redzone-class :required false :type "string" :default "snm-redzone" :validate-fn string? :description "CSS class names, space separated, for the redzone"} + {:name :target-class :required false :type "string" :default "snm-target" + :validate-fn string? :description "CSS class names, space separated, , applied to the frame in an on-target condition"} {:name :unit :required false :type "string" :validate-fn string? :description "Unit to show after the value"} {:name :id :required false :type "string" :default "meter" :validate-fn string? :description "Element id for this instance of the meter"} + {:name :gradations :reduired false :type "integer" :default 5 + :validate-fn integer? :description "Number of gradations to show on the scale, not counting the point."} {:name :style :required false :type "CSS style map" :validate-fn css-style? :description "CSS styles to add or override"} {:name :attr :required false :type "HTML attr map" @@ -81,6 +90,10 @@ ;; 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 range `min-value`...`max-value`." @@ -89,33 +102,97 @@ 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 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 + cartesian coordinates `cx`, `cy`." + [cx cy radius theta] + (let + [in-radians (/ (* (- theta 90) (aget js/Math "PI")) 180.0)] + {: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 + angles in degrees)." + [cx cy radius start-angle end-angle] + (let + [start (polar-to-cartesian cx cy radius start-angle) + end (polar-to-cartesian cx cy radius end-angle) + large-arc? (if (<= (- end-angle start-angle) 180) 0 1) + sweep (if (> end-angle start-angle) 1 0)] + (string/join " " ["M" (:x start) (:y start) "A" radius radius 0 large-arc? sweep (:x end) (:y end)]))) + + +(defn as-label + "If this arg is a floating point number, format it to a reasonable width; else return it." + [arg] + (if + (and (number? arg) (not (integer? arg))) + (.toFixed arg 2) + arg)) + + +(defn gradation + "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)]])) + (defn swinging-needle-meter "Render an SVG swinging needle meter" - [& {:keys [model setpoint width height min-value max-value class alarm-class cursor-class frame-class hub-class needle-class scale-class redzone-class unit id style attr] + [& {: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%" min-value 0 max-value 100 + warn-value 80 + tolerance 3 + gradations 5 + alarm-class "snm-warning" cursor-class "snm-cursor" frame-class "snm-frame" hub-class "snm-hub" needle-class "snm-needle" scale-class "snm-scale" redzone-class "snm-redzone" + target-class "snm-target" id "meter"} :as args}] {:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]} (let [model (deref-or-value model) setpoint (deref-or-value setpoint) - current-value (str model (if unit " ") unit)] + 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)] [box :align :start :child [:div (merge - {:class (str "swinging-needle " class) + {:class (str "swinging-needle " class " " (str + (if (< min-value model warn-value) "" + (str " " alarm-class)) + (if (and (> setpoint min-value)(< (abs (- model setpoint)) tolerance)) + (str " " target-class) ""))) :style (merge (flex-child-style "none") {:width width :height height} style)} @@ -131,37 +208,19 @@ :id id :class (str "snm-meter " class)} [:text - {:xml:space "preserve" - :x "-75.5" - :y "50" - :id (str id "-min-value") - :class "snm-limit" - :transform "matrix(0.2398013,-0.97082199,0.97082199,0.2398013,0,0)"}[:tspan min-value]] - [:text - {:xml:space "preserve" - :x "102" - :y "-102" - :id (str id "-max-value") - :class "snm-limit" - :transform "matrix(0.26614433,0.96393319,-0.96393319,0.26614433,0,0)"}[:tspan max-value]] - [:text - {:xml:space "preserve" - ;; 4.5 here is a real fudge. It's roughly half the width in SVG units of a single character; - ;; it's intended to keep the visible text roughly in the middle of the meter. - :x (str (- 80 (* (count current-value) 4.5))) - :y "60" + {:text-anchor "middle" + :x 80 + :y 70 :width "100" :id (str id "-current-value") - :class "snm-value"}[:tspan current-value]] + :class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]] [:path {:class scale-class :id (str id "-scale") - :d "m 11.85914,76.864488 c 0,0 14.34545,-53.795412 68.140856,-53.795412 53.795424,0 68.140864,53.795412 68.140864,53.795412"}] + :d (describe-arc 80 100 scale-radius (- 0 mid-point-deflection) mid-point-deflection)}] [:path {:class redzone-class :id (str id "-redzone") - :d "m 137.74738,54.878869 c 0,0 3.02675,3.620416 6.3911,11.14347 3.36435,7.523055 4.20612,11.198095 4.20612,11.198095"}] - [:rect {:class (str frame-class (if (< min-value model max-value) "" (str " " alarm-class))) - :id (str id "-frame") - :x "5" :y "5" :height "100" :width "150"}] + :d (describe-arc 80 100 scale-radius (- red-zone-deflection mid-point-deflection) mid-point-deflection)}] + [:path {:class cursor-class :id (str id "-cursor") :d "M 80,20 80,100" @@ -171,11 +230,20 @@ :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) %))) + (range 0 (+ gradations 1))))) + [:rect {:class frame-class + :id (str id "-frame") + :x "5" :y "5" :height "100" :width "150"}] [:circle {:class hub-class :id (str id "-hub") :r "10" :cx "80" :cy "100"}]] - ;;; Useful for debugging: - (str "value: " model "; min: " min-value - "; max: " max-value - "; deflection: " (int (deflection model min-value max-value))) ]])) diff --git a/src/cljs/swinging_needle_meter/views.cljs b/src/cljs/swinging_needle_meter/views.cljs index 21118a4..729d191 100644 --- a/src/cljs/swinging_needle_meter/views.cljs +++ b/src/cljs/swinging_needle_meter/views.cljs @@ -6,14 +6,13 @@ [reagent.core :as reagent])) ;; ------------------------------------------------------------------------------------ -;; Demo: swinging-needle +;; Demo: swinging-needle-meter ;; ------------------------------------------------------------------------------------ (defn swinging-needle-demo [] (let [value (reagent/atom 75) - setpoint (reagent/atom 75) - striped? (reagent/atom false)] + setpoint (reagent/atom 75)] (fn [] [v-box @@ -27,13 +26,27 @@ :width "450px" :children [[title2 "Notes"] [status-text "Wildly experimental"] - [p "An SVG swinging needle meter."] + [p "An SVG swinging needle meter intended to be useful in dashboards."] [p "Note that the cursor will vanish if the setpoint is null or is less than or equal to min-value; this is intentional."] [p "Note that if the value of model is lower then min-value or greater than max-value, it will be limited as it would be on a mechanical meter."] - [p "You can hide the redzone by setting its style to the style 'snm-scale'"] - [p - "TODO: You can't adjust the position of the start of the red-zone; "] + [p "You can hide the redzone by setting its style to the style 'snm-scale', or by setting 'warn-value' equal to 'max-value'."] + + [title2 "Behaviour"] + + [p "min-value and max-value must be numbers; max-value must be greater than min-value. + The default behaviour is of a swinging needle meter with the needle deflection proportional + to the value of the model (also a number) expressed as a proportion of the difference between + min-value and max-value."] + + [p "A red-zone can be introduced by setting a value for warn-value between min-value and max-value. Additionally, if + the value of model exceeds warn-value the class alarm-class will be set on the meter indicating a warning state."] + + [p "A cursor can be shown by setting the value of set-point between min-value and max-value. A tolerance value can be specified + by setting a value for tolerance. If the difference between the model value and the setpoint value is less than the tolerance + value, the class target-class will be set on the meter to indicate an on-target status. The setpoint value, like the model value, + may change dynamically at run-time."] + [args-table swinging-needle-args-desc]]] [v-box :gap "10px" @@ -43,10 +56,13 @@ :children [[swinging-needle-meter :model value :setpoint setpoint -;; :unit "Mw" + :unit "Mw" ;; :min-value 20 +;; :warn-value 35 ;; :max-value 40 -;; :alarm-class "snm-warning" +;; :max-value (aget js/Math "PI") + :tolerance 2 + :alarm-class "snm-warning" :width "350px"] [title :level :level3 :label "Parameters"] [h-box