Merge branch 'release/1.0.1'
This commit is contained in:
commit
45865eaae2
15
project.clj
15
project.clj
|
@ -1,11 +1,12 @@
|
||||||
(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."
|
: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"]
|
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||||
[org.clojure/clojurescript "1.9.229"]
|
[org.clojure/clojurescript "1.9.229"]
|
||||||
[reagent "0.6.0"]
|
[reagent "0.6.0"]
|
||||||
[re-frame "0.9.4"]
|
[re-frame "0.9.4"]
|
||||||
[org.clojure/core.async "0.2.391"]
|
[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"]]
|
:plugins [[lein-cljsbuild "1.1.4"]]
|
||||||
|
|
||||||
|
@ -20,9 +21,7 @@
|
||||||
:profiles
|
:profiles
|
||||||
{:dev
|
{:dev
|
||||||
{:dependencies [[binaryage/devtools "0.8.2"]]
|
{:dependencies [[binaryage/devtools "0.8.2"]]
|
||||||
|
:plugins [[lein-figwheel "0.5.9"]]}}
|
||||||
:plugins [[lein-figwheel "0.5.9"]]
|
|
||||||
}}
|
|
||||||
|
|
||||||
:cljsbuild
|
:cljsbuild
|
||||||
{:builds
|
{:builds
|
||||||
|
@ -44,8 +43,4 @@
|
||||||
:output-to "resources/public/js/compiled/app.js"
|
:output-to "resources/public/js/compiled/app.js"
|
||||||
:optimizations :advanced
|
:optimizations :advanced
|
||||||
:closure-defines {goog.DEBUG false}
|
:closure-defines {goog.DEBUG false}
|
||||||
:pretty-print false}}
|
:pretty-print false}}]})
|
||||||
|
|
||||||
|
|
||||||
]}
|
|
||||||
)
|
|
||||||
|
|
BIN
resources/public/css/chosen-sprite.png
Normal file
BIN
resources/public/css/chosen-sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 646 B |
|
@ -8,13 +8,13 @@
|
||||||
|
|
||||||
.snm-cursor {
|
.snm-cursor {
|
||||||
stroke:#ff8500;
|
stroke:#ff8500;
|
||||||
stroke-width:5;
|
stroke-width: 3%;
|
||||||
stroke-opacity: 0.5;
|
stroke-opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-frame {
|
.snm-frame {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 10;
|
stroke-width: 5%;
|
||||||
stroke-linejoin: round;
|
stroke-linejoin: round;
|
||||||
stroke: #444444;
|
stroke: #444444;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-gradation text {
|
.snm-gradation text {
|
||||||
font-size: 50%;
|
font-size: 150%;
|
||||||
font-weight: lighter;
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,11 +33,6 @@
|
||||||
fill: #444444;
|
fill: #444444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-limit {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.snm-meter {
|
.snm-meter {
|
||||||
height: 50%;
|
height: 50%;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -51,13 +46,13 @@
|
||||||
.snm-redzone {
|
.snm-redzone {
|
||||||
fill:none;
|
fill:none;
|
||||||
stroke: maroon;
|
stroke: maroon;
|
||||||
stroke-width: 15;
|
stroke-width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-scale {
|
.snm-scale {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: green;
|
stroke: green;
|
||||||
stroke-width: 15;
|
stroke-width: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-target .snm-frame {
|
.snm-target .snm-frame {
|
||||||
|
@ -65,6 +60,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.snm-value {
|
.snm-value {
|
||||||
|
font-size: 200%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<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 src="js/compiled/app.js"></script>
|
||||||
<script>swinging_needle_meter.core.init();</script>
|
<script>swinging_needle_meter.core.init();</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
(ns swinging-needle-meter.core
|
(ns swinging-needle-meter.core
|
||||||
(:require [reagent.core :as reagent]
|
(:require [reagent.core :as reagent]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as rf]
|
||||||
[swinging-needle-meter.events]
|
[swinging-needle-meter.events]
|
||||||
[swinging-needle-meter.subs]
|
[swinging-needle-meter.subs]
|
||||||
[swinging-needle-meter.views :as views]
|
[swinging-needle-meter.views :as views]
|
||||||
|
@ -14,12 +14,20 @@
|
||||||
(enable-console-print!)
|
(enable-console-print!)
|
||||||
(println "dev mode")))
|
(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 []
|
(defn mount-root []
|
||||||
(re-frame/clear-subscription-cache!)
|
(rf/clear-subscription-cache!)
|
||||||
(reagent/render [views/main-panel]
|
(reagent/render [views/main-panel]
|
||||||
(.getElementById js/document "app")))
|
(.getElementById js/document "app")))
|
||||||
|
|
||||||
(defn ^:export init []
|
(defn ^:export init []
|
||||||
(re-frame/dispatch-sync [:initialize-db])
|
(rf/dispatch-sync [:initialize-db])
|
||||||
(dev-setup)
|
(dev-setup)
|
||||||
(mount-root))
|
(mount-root))
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
(ns swinging-needle-meter.db)
|
|
||||||
|
|
||||||
(def default-db
|
|
||||||
{:name "re-frame"})
|
|
|
@ -1,10 +1,62 @@
|
||||||
(ns swinging-needle-meter.events
|
(ns swinging-needle-meter.events
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[swinging-needle-meter.db :as db]))
|
[swinging-needle-meter.state :as state]))
|
||||||
|
|
||||||
;;; This file is unchanged (except this line) from the leiningen recom template
|
|
||||||
|
|
||||||
|
;; Reset.
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
:initialize-db
|
:initialize-db
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
db/default-db))
|
state/default-state))
|
||||||
|
|
||||||
|
;; The clock ticked. Implement a mechanical swing.
|
||||||
|
(re-frame/reg-event-db
|
||||||
|
:timer
|
||||||
|
(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]]
|
||||||
|
(assoc
|
||||||
|
(assoc db :old-value (:value 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)))
|
||||||
|
|
||||||
|
|
15
src/cljs/swinging_needle_meter/state.cljs
Normal file
15
src/cljs/swinging_needle_meter/state.cljs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
(ns ^{:doc "Client state."
|
||||||
|
:author "Simon Brooke"}
|
||||||
|
swinging-needle-meter.state)
|
||||||
|
|
||||||
|
(def default-state
|
||||||
|
{:timer (js/Date.)
|
||||||
|
:value 60
|
||||||
|
:old-value 0
|
||||||
|
:setpoint 75
|
||||||
|
:gradations 5
|
||||||
|
:size 70
|
||||||
|
:min-val 0
|
||||||
|
:max-val 100
|
||||||
|
:warn-val 80
|
||||||
|
:unit "Mw"})
|
|
@ -2,9 +2,58 @@
|
||||||
(:require-macros [reagent.ratom :refer [reaction]])
|
(:require-macros [reagent.ratom :refer [reaction]])
|
||||||
(:require [re-frame.core :as re-frame]))
|
(:require [re-frame.core :as re-frame]))
|
||||||
|
|
||||||
;;; This file is unchanged (except this line) from the leiningen recom template
|
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:name
|
:name
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(:name db)))
|
(:name db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:value
|
||||||
|
(fn [db]
|
||||||
|
(:value db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:old-value
|
||||||
|
(fn [db]
|
||||||
|
(:old-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)))
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
[re-com.box :refer [flex-child-style]]
|
[re-com.box :refer [flex-child-style]]
|
||||||
[re-com.util :refer [deref-or-value]]
|
[re-com.util :refer [deref-or-value]]
|
||||||
[re-com.validate :refer [number-or-string? css-style? html-attr? validate-args-macro]]
|
[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]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;
|
;;;;
|
||||||
|
@ -41,10 +42,10 @@
|
||||||
:validate-fn number-or-string? :description "current value of the variable being watched. A number between 0 and 100"}
|
: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"
|
{: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"}
|
: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%"
|
{:name :width :required false :type "integer" :default "300"
|
||||||
:validate-fn string? :description "a CSS width"}
|
:validate-fn integer? :description "a CSS width"}
|
||||||
{:name :height :required false :type "string" :default "100%"
|
{:name :height :required false :type "integer" :default "200"
|
||||||
:validate-fn string? :description "a CSS height"}
|
:validate-fn integer? :description "a CSS height"}
|
||||||
{:name :min-value :required false :type "double" :default 0
|
{:name :min-value :required false :type "double" :default 0
|
||||||
:validate-fn number? :description "the minimum value model can take"}
|
:validate-fn number? :description "the minimum value model can take"}
|
||||||
{:name :max-value :required false :type "double" :default 100
|
{:name :max-value :required false :type "double" :default 100
|
||||||
|
@ -82,29 +83,21 @@
|
||||||
{:name :attr :required false :type "HTML attr map"
|
{: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"]}])
|
: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
|
;; 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)
|
||||||
|
|
||||||
;; ultimately this should be resizeable, and radius should be a function of
|
|
||||||
;; size...
|
|
||||||
(def scale-radius 75)
|
|
||||||
|
|
||||||
(defn deflection
|
(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`."
|
range `min-value`...`max-value`."
|
||||||
[value min-value max-value]
|
[value min-value max-value]
|
||||||
(let [range (- max-value min-value)
|
(let [range (- max-value min-value)
|
||||||
deflection (/ value range)
|
|
||||||
zero-offset (/ (- 0 min-value) range)
|
zero-offset (/ (- 0 min-value) range)
|
||||||
limited (min (max (+ zero-offset deflection) 0) 1)]
|
limited (min (max (+ zero-offset (/ value range)) 0) 1)]
|
||||||
(js/console.log (str "zero-offset: " zero-offset))
|
|
||||||
(* (- limited 0.5) full-scale-deflection)))
|
(* (- limited 0.5) full-scale-deflection)))
|
||||||
|
|
||||||
|
|
||||||
(defn polar-to-cartesian
|
(defn polar-to-cartesian
|
||||||
"Return, as a map with keys :x. :y, the cartesian coordinates at the point
|
"Return, as a map with keys :x. :y, the cartesian coordinates at the point
|
||||||
`radius` distance at `theta` (degrees) angle from a point at
|
`radius` distance at `theta` (degrees) angle from a point at
|
||||||
|
@ -115,6 +108,7 @@
|
||||||
{:x (+ cx (* radius (.cos js/Math in-radians)))
|
{:x (+ cx (* radius (.cos js/Math in-radians)))
|
||||||
:y (+ cy (* radius (.sin js/Math in-radians)))}))
|
:y (+ cy (* radius (.sin js/Math in-radians)))}))
|
||||||
|
|
||||||
|
|
||||||
(defn describe-arc
|
(defn describe-arc
|
||||||
"Return as a string an SVG path definition describing an arc centred
|
"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
|
at `cx`, cy` starting at `start-angle` and ending at `end-angle` (both
|
||||||
|
@ -141,25 +135,34 @@
|
||||||
"Return as a string an SVG path definition describing a radial stroke from a center
|
"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`."
|
at `cx`, cy` starting at `min-radius` and extending to `max-radius`."
|
||||||
[cx cy min-radius max-radius angle label]
|
[cx cy min-radius max-radius angle label]
|
||||||
(let
|
[:g {:class "snm-gradation"
|
||||||
[start (polar-to-cartesian cx cy min-radius angle)
|
:transform (string/join " " ["rotate(" angle cx cy ")"])}
|
||||||
mid (polar-to-cartesian cx cy (+ min-radius
|
[:path {:d (string/join
|
||||||
(* (- max-radius min-radius) 0.333))
|
" "
|
||||||
angle)
|
["M"
|
||||||
end (polar-to-cartesian cx cy max-radius angle)]
|
cx
|
||||||
[:g {:class "snm-gradation"}
|
(- cy
|
||||||
[:path {:d (string/join " " ["M" (:x mid) (:y mid) "L" (:x end) (:y end)])}]
|
(+ min-radius
|
||||||
[:text {:text-anchor "middle"
|
(* (- max-radius min-radius) 0.333)))
|
||||||
:x (:x start)
|
"L"
|
||||||
:y (:y start)
|
cx
|
||||||
:transform (string/join " " ["rotate(" angle (:x start) (:y start) ")"])} (as-label label)]]))
|
(- cy max-radius)])}]
|
||||||
|
[:text {:text-anchor "middle"
|
||||||
|
:x cx
|
||||||
|
: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
|
(defn swinging-needle-meter
|
||||||
"Render an SVG 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]
|
[& {: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%"
|
:or {width 300
|
||||||
height "100%"
|
height 200
|
||||||
min-value 0
|
min-value 0
|
||||||
max-value 100
|
max-value 100
|
||||||
warn-value 80
|
warn-value 80
|
||||||
|
@ -179,11 +182,11 @@
|
||||||
(let [model (deref-or-value model)
|
(let [model (deref-or-value model)
|
||||||
setpoint (deref-or-value setpoint)
|
setpoint (deref-or-value setpoint)
|
||||||
mid-point-deflection (/ full-scale-deflection 2)
|
mid-point-deflection (/ full-scale-deflection 2)
|
||||||
;; if warn-value is greater than max-value, we don't want a red-zone at all.
|
cx (/ width 2)
|
||||||
red-zone-deflection (if
|
cy (* height 0.90)
|
||||||
(< warn-value max-value)
|
needle-length (* height 0.75)
|
||||||
(* full-scale-deflection (/ warn-value max-value))
|
scale-radius (* height 0.7)
|
||||||
full-scale-deflection)]
|
gradation-inner (* height 0.55)]
|
||||||
[box
|
[box
|
||||||
:align :start
|
:align :start
|
||||||
:child [:div
|
:child [:div
|
||||||
|
@ -197,53 +200,58 @@
|
||||||
{:width width :height height}
|
{:width width :height height}
|
||||||
style)}
|
style)}
|
||||||
attr)
|
attr)
|
||||||
[:svg {:xmlns:svg "http://www.w3.org/2000/svg"
|
[:svg {:xmlSpace "preserve"
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
|
||||||
:xml:space "preserve"
|
|
||||||
:overflow "visible"
|
:overflow "visible"
|
||||||
:viewBox "0 0 180 120"
|
:viewBox (string/join " " [0 0 width height])
|
||||||
|
:width (str width "px")
|
||||||
|
:height (str height "px")
|
||||||
:y "0px"
|
:y "0px"
|
||||||
:x "0px"
|
:x "0px"
|
||||||
:version "1.1"
|
:version "1.1"
|
||||||
:id id
|
:id id
|
||||||
:class (str "snm-meter " class)}
|
:class (str "snm-meter " class)}
|
||||||
|
|
||||||
[:text
|
[:text
|
||||||
{:text-anchor "middle"
|
{:text-anchor "middle"
|
||||||
:x 80
|
:x (/ width 2)
|
||||||
:y 70
|
:y (/ height 2)
|
||||||
:width "100"
|
:width "100"
|
||||||
:id (str id "-current-value")
|
:id (str id "-current-value")
|
||||||
:class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]]
|
:class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]]
|
||||||
[:path {:class scale-class
|
[:path {:class scale-class
|
||||||
:id (str id "-scale")
|
:id (str id "-scale")
|
||||||
:d (describe-arc 80 100 scale-radius (- 0 mid-point-deflection) mid-point-deflection)}]
|
: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
|
[:path {:class redzone-class
|
||||||
:id (str id "-redzone")
|
:id (str id "-redzone")
|
||||||
:d (describe-arc 80 100 scale-radius (- red-zone-deflection mid-point-deflection) mid-point-deflection)}]
|
: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
|
[:path {:class cursor-class
|
||||||
:id (str id "-cursor")
|
: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")
|
: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
|
[:path {:class needle-class
|
||||||
:id (str id "-needle")
|
:id (str id "-needle")
|
||||||
:d "M 80,20 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) ", 80, 100)") }]
|
:transform (str "rotate( " (deflection model min-value max-value) "," cx "," cy ")") }]
|
||||||
(apply vector (cons :g (map #(gradation 80 100 60 82
|
(if (> gradations 0)
|
||||||
(- (* %
|
(apply vector (cons :g (map #(let
|
||||||
(/ full-scale-deflection gradations))
|
[value (+ min-value
|
||||||
mid-point-deflection)
|
|
||||||
(+ min-value
|
|
||||||
(*
|
(*
|
||||||
(/
|
(/
|
||||||
(- max-value min-value)
|
(- max-value min-value)
|
||||||
gradations) %)))
|
gradations) %))]
|
||||||
(range 0 (+ gradations 1)))))
|
(gradation cx cy gradation-inner needle-length
|
||||||
|
(deflection value min-value max-value)
|
||||||
|
value))
|
||||||
|
(range 0 (+ gradations 1))))))
|
||||||
[:rect {:class frame-class
|
[:rect {:class frame-class
|
||||||
:id (str id "-frame")
|
: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
|
[:circle {:class hub-class
|
||||||
:id (str id "-hub")
|
:id (str id "-hub")
|
||||||
:r "10" :cx "80" :cy "100"}]]
|
:r (/ height 10) :cx cx :cy cy}]]
|
||||||
]]))
|
]]))
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
(ns swinging-needle-meter.utils
|
(ns swinging-needle-meter.utils
|
||||||
(:require [re-com.core :refer [h-box v-box box gap title line label hyperlink-href align-style]]))
|
(: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.
|
;;;; I claim no credit for it.
|
||||||
|
|
||||||
|
|
||||||
|
(defn abs
|
||||||
|
"Return the absolute value of the (numeric) argument."
|
||||||
|
[n] (max n (- n)))
|
||||||
|
|
||||||
|
|
||||||
(defn github-hyperlink
|
(defn github-hyperlink
|
||||||
"given a label and a relative path, return a component which hyperlinks to the GitHub URL in a new tab"
|
"given a label and a relative path, return a component which hyperlinks to the GitHub URL in a new tab"
|
||||||
[label src-path]
|
[label src-path]
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
(ns swinging-needle-meter.views
|
(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]]
|
[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.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]]
|
[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
|
;; Demo: swinging-needle-meter
|
||||||
;; ------------------------------------------------------------------------------------
|
;; ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
(defn swinging-needle-demo
|
(defn swinging-needle-demo
|
||||||
[]
|
[]
|
||||||
(let [value (reagent/atom 75)
|
(let [unit @(rf/subscribe [:unit])
|
||||||
setpoint (reagent/atom 75)]
|
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
|
(fn
|
||||||
[]
|
[]
|
||||||
[v-box
|
[v-box
|
||||||
|
@ -54,37 +61,99 @@
|
||||||
[v-box
|
[v-box
|
||||||
:gap "20px"
|
:gap "20px"
|
||||||
:children [[swinging-needle-meter
|
:children [[swinging-needle-meter
|
||||||
:model value
|
:model @(rf/subscribe [:old-value])
|
||||||
:setpoint setpoint
|
:setpoint @(rf/subscribe [:setpoint])
|
||||||
:unit "Mw"
|
:unit @(rf/subscribe [:unit])
|
||||||
;; :min-value 20
|
:min-value @(rf/subscribe [:min-val])
|
||||||
;; :warn-value 35
|
:warn-value @(rf/subscribe [:warn-val])
|
||||||
;; :max-value 40
|
:max-value @(rf/subscribe [:max-val])
|
||||||
;; :max-value (aget js/Math "PI")
|
|
||||||
:tolerance 2
|
:tolerance 2
|
||||||
:alarm-class "snm-warning"
|
:alarm-class "snm-warning"
|
||||||
:width "350px"]
|
:gradations @(rf/subscribe [:gradations])
|
||||||
|
:height (int (* @(rf/subscribe [:size]) 6))
|
||||||
|
:width (int (* @(rf/subscribe [:size]) 10))]
|
||||||
[title :level :level3 :label "Parameters"]
|
[title :level :level3 :label "Parameters"]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":model"]]
|
:children [[box :align :start :child [:code ":model"]]
|
||||||
[slider
|
[slider
|
||||||
:model value
|
:model @(rf/subscribe [:value])
|
||||||
:min 0
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! value %)]
|
:on-change #(rf/dispatch [:set-value %])]
|
||||||
[label :label @value]]]
|
[label :label @(rf/subscribe [:value])]]]
|
||||||
[h-box
|
[h-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":setpoint"]]
|
:children [[box :align :start :child [:code ":setpoint"]]
|
||||||
[slider
|
[slider
|
||||||
:model setpoint
|
:model @(rf/subscribe [:setpoint])
|
||||||
:min 0
|
:min -100
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(reset! setpoint %)]
|
:on-change #(rf/dispatch [:set-setpoint %])]
|
||||||
[label :label @setpoint]]]
|
[label :label @(rf/subscribe [:setpoint])]]]
|
||||||
|
[h-box
|
||||||
|
:gap "10px"
|
||||||
|
:children [[box :align :start :child [:code ":min-val"]]
|
||||||
|
[slider
|
||||||
|
:model @(rf/subscribe [:min-val])
|
||||||
|
:min -100
|
||||||
|
:max 100
|
||||||
|
:width "200px"
|
||||||
|
: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"]]
|
||||||
|
[slider
|
||||||
|
:model @(rf/subscribe [:max-val])
|
||||||
|
:min -100
|
||||||
|
:max 100
|
||||||
|
:width "200px"
|
||||||
|
:on-change #(rf/dispatch [:set-max-value %])]
|
||||||
|
[label :label @(rf/subscribe [:max-val])]]]
|
||||||
|
[h-box
|
||||||
|
:gap "10px"
|
||||||
|
:children [[box :align :start :child [:code ":warn-val"]]
|
||||||
|
[slider
|
||||||
|
:model @(rf/subscribe [:warn-val])
|
||||||
|
:min -100
|
||||||
|
:max 100
|
||||||
|
:width "200px"
|
||||||
|
:on-change #(rf/dispatch [:set-warning-value %])]
|
||||||
|
[label :label @(rf/subscribe [:warn-val])]]]
|
||||||
|
[h-box
|
||||||
|
:gap "10px"
|
||||||
|
:children [[box :align :start :child [:code ":gradations"]]
|
||||||
|
[slider
|
||||||
|
:model @(rf/subscribe [:gradations])
|
||||||
|
:min 0
|
||||||
|
:max 10
|
||||||
|
:width "200px"
|
||||||
|
:on-change #(rf/dispatch [:set-gradations %])]
|
||||||
|
[label :label @(rf/subscribe [:gradations])]]]
|
||||||
|
[h-box
|
||||||
|
:gap "10px"
|
||||||
|
:children [[box :align :start :child [:code ":unit"]]
|
||||||
|
[single-dropdown
|
||||||
|
: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 #(rf/dispatch [:set-unit %])]]]
|
||||||
|
[h-box
|
||||||
|
:gap "10px"
|
||||||
|
:children [[box :align :start :child [:code ":size"]]
|
||||||
|
[slider
|
||||||
|
:model size
|
||||||
|
:min 25
|
||||||
|
:max 100
|
||||||
|
:width "200px"
|
||||||
|
:on-change #(rf/dispatch [:set-size %])]
|
||||||
|
[label :label size]]]
|
||||||
]]]]]]]])))
|
]]]]]]]])))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue