All features except adjustable redzone.

This commit is contained in:
Simon Brooke 2017-06-30 19:12:31 +01:00
parent dcda9d8979
commit 4f1a97d731
4 changed files with 1421 additions and 29 deletions

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
<meta charset='utf-8'>
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="vendor/css/material-design-iconic-font.min.css">
<link rel="stylesheet" href="vendor/css/re-com.css">
<link rel="stylesheet" href="css/re-com.css">
<link rel="stylesheet" href="css/swinging-needle-meter.css">
<link href="http://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic" rel="stylesheet" type="text/css">
<link href="http://fonts.googleapis.com/css?family=Roboto+Condensed:400,300" rel="stylesheet" type="text/css">

View file

@ -9,6 +9,8 @@
;; Component: swinging-needle
;; ------------------------------------------------------------------------------------
;;; It seems the defaults given here are just documentation; the defaults
;;; that are actually used are those given in the :or clause of the argument map.
(def swinging-needle-args-desc
[{:name :model :required true :type "double | atom"
:validate-fn number-or-string? :description "current value of the variable being watched. A number between 0 and 100"}
@ -18,29 +20,61 @@
:validate-fn string? :description "a CSS width"}
{:name :height :required false :type "string" :default "100%"
:validate-fn string? :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
:validate-fn number? :description "the maximum value model can take"}
{:name :class :required false :type "string"
:validate-fn string? :description "CSS class names, space separated"}
;; {: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"
;; :validate-fn string? :description "CSS class names, space separated, for the frame"}
;; {:name :hub-class :required false :type "string" :default "snm-hub"
;; :validate-fn string? :description "CSS class names, space separated, for the hub"}
;; {:name :needle-class :required false :type "string" :default "snm-needle"
;; :validate-fn string? :description "CSS class names, space separated, for the needle"}
;; {:name :scale-class :required false :type "string" :default "snm-scale"
;; :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 :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"
:validate-fn string? :description "CSS class names, space separated, for the frame"}
{:name :hub-class :required false :type "string" :default "snm-hub"
:validate-fn string? :description "CSS class names, space separated, for the hub"}
{:name :needle-class :required false :type "string" :default "snm-needle"
:validate-fn string? :description "CSS class names, space separated, for the needle"}
{:name :scale-class :required false :type "string" :default "snm-scale"
: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 :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"
: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)
(defn deflection
"Return the 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)]
(* (- limited 0.5) full-scale-deflection)))
(defn swinging-needle-meter
"Render an SVG swinging needle meter"
[& {:keys [model setpoint width height class cursor-class frame-class hub-class needle-class scale-class redzone-class style attr]
:or {width "100%" height "100%"}
[& {:keys [model setpoint width height min-value max-value class cursor-class frame-class hub-class needle-class scale-class redzone-class style attr]
:or {width "100%"
height "100%"
min-value 0
max-value 100
cursor-class "snm-cursor"
frame-class "snm-frame"
hub-class "snm-hub"
needle-class "snm-needle"
scale-class "snm-scale"
redzone-class "snm-redzone"}
:as args}]
{:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]}
(let [model (deref-or-value model)
@ -66,16 +100,20 @@
:x "0px"
:version "1.1"
:class (str "snm-meter " class)}
[:path {:class "snm-scale"
[:path {:class scale-class
: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"}]
[:path {:class "snm-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 "snm-frame" :x "5" :y "5" :height "100" :width "150"}]
[:path {:class "snm-cursor"
[:path {:class redzone-class :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 frame-class :x "5" :y "5" :height "100" :width "150"}]
[:path {:class cursor-class
:d "M 80,20 80,100"
:visibility (if (and (number? setpoint) (> setpoint 0)) "visible" "hidden")
:transform (str "rotate( " (* (- setpoint 50) 1.4) ", 80, 100)")}]
[:path {:class "snm-needle"
:visibility (if (and (number? setpoint) (> setpoint min-value)) "visible" "hidden")
:transform (str "rotate( " (deflection setpoint min-value max-value) ", 80, 100)")}]
[:path {:class needle-class
:d "M 80,20 80,100"
:transform (str "rotate( " (* (- model 50) 1.4) ", 80, 100)") }]
[:circle {:class "snm-hub" :r "10" :cx "80" :cy "100"}]]
(str model "%")]]]))
:transform (str "rotate( " (deflection model min-value max-value) ", 80, 100)") }]
[:circle {:class hub-class :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)))
]]]))

View file

@ -27,11 +27,13 @@
:width "450px"
:children [[title2 "Notes"]
[status-text "Wildly experimental"]
[p "An SVG swinging needle meter. Note that the cursor will vanish if the setpoint is null or zero; this is intentional."]
[p "An SVG swinging needle meter."]
[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; "
"you can't override the classes for the different elements of the meter; "
"you can't override the maximum and minimum values."]
"TODO: You can't adjust the position of the start of the red-zone; "]
[args-table swinging-needle-args-desc]]]
[v-box
:gap "10px"