Compare commits

..

21 commits

Author SHA1 Message Date
Simon Brooke 6a889b247e
Removed unnecessary stuff from Usage in README 2020-10-24 18:38:02 +01:00
Simon Brooke fdffaa0c92
Set 'status' to 'stable' 2020-10-24 17:45:09 +01:00
Simon Brooke 4ae79d3bae
'size' slider on demo page now works correctly! 2020-10-24 17:29:15 +01:00
Simon Brooke 9c335ca453
Greatly improved README; upversioned to 1.0.3 2020-10-24 17:16:02 +01:00
Simon Brooke 7020fa4203
Added credit logos to footer, primarily for find me/fork me link. 2020-10-24 15:37:21 +01:00
Simon Brooke 293e911556
Removed an awful lot of compiled javascript files from the repo
Demo still seems to work.
2020-10-24 15:04:23 +01:00
Simon Brooke e3b5963e55
Removed unused code 2020-10-24 14:46:19 +01:00
Simon Brooke 642662cc82
Copyright date, only 2020-10-24 13:20:02 +01:00
Simon Brooke e9e3dd6a8d Added a wee 'loading screen' message. 2020-10-23 22:02:38 +01:00
Simon Brooke dc226b1f25 Added compiled JavaScript to repository for GitHub pages
This feels like a mistake...
2020-10-20 14:44:11 +01:00
Simon Brooke 3d5a2fb322 Upversion to 1.0.2
With luck, this gives us a working GitHub pages demo
2020-10-20 14:36:16 +01:00
Simon Brooke 636e6466c0 Merge branch 'with-demo-site' 2020-10-20 14:35:02 +01:00
Simon Brooke ec9a0803f4 OK, Figwheel requires webroot under resources. Bother.
Bother.
2020-10-20 14:34:26 +01:00
Simon Brooke 26a60fb1fe Works from filesystem in docs directory but figwheel does not serve 2020-10-20 13:47:54 +01:00
Simon Brooke 4468c155d3 Moved everything from 'resources/public' to 'docs' 2020-10-20 13:08:53 +01:00
simon 57b8abf0d3 Minor fix. 2020-10-20 13:08:53 +01:00
simon 45865eaae2 Merge branch 'release/1.0.1' 2017-07-18 14:21:00 +01:00
simon ef4653e3b2 Minor fix. 2017-07-18 14:20:38 +01:00
simon 689f1b108c Merge remote-tracking branch 'origin/master' 2017-07-09 16:14:41 +01:00
simon 832a3eb2db Merge branch 'release/1.0.0' 2017-07-09 16:14:11 +01:00
simon 589dbbb36b Merge branch 'release/1.0.0' 2017-07-09 16:07:14 +01:00
16 changed files with 1110 additions and 45 deletions

2
.gitignore vendored
View file

@ -1,7 +1,5 @@
/*.log
/target
/*-init.clj
/resources/public/js/compiled
out
resources/public/vendor/*

View file

@ -6,6 +6,10 @@ Works well in Chrome and Firefox; not tested in Internet Exploder.
![what it should look like](resources/public/images/example.png)
## Demo site
There's a demo site [here](https://simon-brooke.github.io/swinging-needle-meter/resources/public/index.html).
## Intended uses
This is a component for a console, typically one controlling a technical or scientific instrument. It is
@ -16,6 +20,63 @@ A cursor will be shown if the value of *setpoint* is between *min-value* and *ma
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).
## Usage
In your cljs file, require the following:
```clojure
(:require [re-frame.core :as rf]
[swinging-needle-meter.swinging-needle-meter :refer [swinging-needle-meter]])
```
within a [re-com](https://github.com/day8/re-com) component,
```clojure
[swinging-needle-meter
:model @(rf/subscribe [:old-value])
:setpoint @(rf/subscribe [:setpoint]) ;; optional
:unit @(rf/subscribe [:unit]) ;; optional
:min-value @(rf/subscribe [:min-val]) ;; optional; default 0
:warn-value @(rf/subscribe [:warn-val]) ;; optional; default 80
:max-value @(rf/subscribe [:max-val]) ;; optional; default 100
:tolerance 2 ;; optional; default 3
:alarm-class "snm-warning" ;; optional; default "snm-warning"
:gradations @(rf/subscribe [:gradations]) ;; optional; default 5
:height (int (* @(rf/subscribe [:size]) 6)) ;; optional; default 200 (pixels)
:width (int (* @(rf/subscribe [:size]) 10))];; optional; default 300 (pixels)
```
or, minimally, just
```clojure
[swinging-needle-meter
:model @(rf/subscribe [:old-value])]
```
There are further arguments which may be set which are documented
[here](https://simon-brooke.github.io/swinging-needle-meter/resources/public/index.html#parameters).
Obviously, all the subscriptions above must be registered with `re-frame/reg-sub`. See [re-frame documentation](http://day8.github.io/re-frame/re-frame/).
The value subscribed to as the value to `:model` is expected to be a floating point number between that of `:min-value` and `:max-value`.
### Simulation of a mechanical meter needle with inertia and damping
You don't need animated movement, you can simply jerk the needle to its new position; animation appeals to users who are used to mechanical meters and is easy on the eye, but obviously it means the needle lags a little behind changes in the underlying state.
If you want animation, this is how it works.
The event registered to be driven by `:timer` in `swinging-needle-meter/events.cljs` drives the animation of the movement of the needle. The value of `:timer` is initialised in the state to `(js/Date.)` in `swinging-needle-meter/state.js`. Thus, it's a clock that ticks.
The actual value in the state which is tracked by the meter is the value of, in the example, `:target`. The `:timer` event moves `:old-value` progressively towards `:target` until they coincide. So in a real deployment you'd poll the actual real world value that you were tracking using a repeated asynchronous JSON request, and, on a response to such a request, you would update the value of `:target` in the state.
Obviously, if you want to put multiple meters onto one page tracking different real world variables, you would minimally have to have a separate key in the state for
1. The `target` of each meter;
2. the `old-value` of each meter (if animation is desired);
You might also want a separate key for the `warn-value` of each meter, and possibly also the `set-point`. Values of other parameters may be subscribed to from values in the state but it will probably be more convenient to hard-wire them or allow them to default.
## Development Mode
### Run application:
@ -31,7 +92,6 @@ Wait a bit, then browse to [http://localhost:3449](http://localhost:3449).
## Production Build
To compile clojurescript to javascript:
```
@ -42,5 +102,5 @@ lein cljsbuild once min
## License
Copyright © 2017 Simon Brooke. Licensed under the GNU General Public License,
version 2.0 or (at your option) any later version. If you wish to incorporate
parts of Smeagol into another open source project which uses a less restrictive
parts of this into another open source project which uses a less restrictive
license, please contact me; I'm open to dual licensing it.

24
index.html Normal file
View file

@ -0,0 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta http-equiv="refresh" content="0; URL='resources/public/index.html'" />
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="resources/public/css/re-com.css">
<link rel="stylesheet" href="resources/public/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">
<title>Example swinging needle meter following re-com conventions.</title>
</head>
<body>
<div id="app">
<h1>
This isn't actually where you want to be.
</h1>
<p>
If you're seeing this, you probably want to <a href="resources/public/index.html">go here.</a>
</p>
</div>
</body>
</html>

View file

@ -1,4 +1,4 @@
(defproject swinging-needle-meter "1.0.0"
(defproject swinging-needle-meter "1.0.3"
: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"]

View file

@ -26,7 +26,7 @@ html, body {
letter-spacing: 0.002em;
height: 100%;
margin: 0px;
padding: 0px;
padding: 0px 5%;
}
/*----------------------------------------------------------------------------------------

View file

@ -0,0 +1,282 @@
/*
* Standard.css copied from Smeagol: a very simple Wiki engine
* Copyright (C) 2014 Simon Brooke
* 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.
*/
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
/* ids generally in document order */
/* top-of-page navigation, not editable, provided by Smeagol */
#nav{
margin: 0;
padding: 0;
top: 0;
width: 100%;
_position: absolute;
_top: expression(document.documentElement.scrollTop);
z-index: 149;
background:rgba(40, 40, 40,0.8);
}
#user {
font-height: 66%;
float: right;
padding: 0.1em 0.75em;
margin: 0;
color: white;
}
#user a {
color: silver;
}
/* only needed for fly-out menu effect on tablet and phone stylesheets */
#nav-icon {
display: none;
}
#nav-menu {
margin: 0;
padding: 0;
}
#nav ul li {
padding: 0;
margin: 0;
display: inline;
}
#nav ul li a {
color: white;
text-decoration: none;
font-weight: bold;
padding: 0.1em 0.75em;
margin: 0;
}
#nav ul li.active a { background: silver;}
li.nav-item a:hover { background: rgb( 240, 240, 240) }
li.nav-item a:active { background: gray; color: white; }
/* header for all pages in the Wiki - editable, provided by users. Within main-container */
#header {
margin-top: 0;
width:100%;
background-color: gray;
color: white;
}
#header h1 {
margin-top: 0;
}
/* left bar for all pages in the Wiki - editable, provided by users. Within main-container */
#left-bar {
width: 17%;
height: 100%;
float: left;
}
/* content of the current in the Wiki - editable, provided by users. Within main-container */
#content {
border: thin solid silver;
width: 80%;
float: right;
padding-bottom: 5em;
}
/* cookies information box, fixed, in right margin, just above footer */
#cookies {
width: 30%;
float: right;
position: fixed;
bottom: 1.5em;
right: 0;
z-index: 150;
background: transparent;
}
/* about-cookies box: permanently visible part of cookies information box */
#about-cookies {
clear: right;
width: 10em;
font-size: 66%;
float: right;
text-align: right;
padding: 0.25em 2em;
color: white;
background:rgba(40,40,40,0.8);
}
/* more-about-cookies box, normally hidden */
#more-about-cookies {
display: none;
padding: 0.25em 2em;
color: white;
background:rgba(40,40,40,0.8);
border-bottom: thin solid white;
}
/* but magically appears on mouseover */
#cookies:hover #more-about-cookies {
display: block;
}
/* footer of the page - not-editable, provided by Smeagol */
#footer {
clear: both;
font-size: smaller;
text-align: center;
color:white;
background:rgba(128,128,128,0.95);
width: 100%;
margin: 0;
padding: 0.25em 0;
bottom:0;
position:fixed;
vertical-align: top;
z-index:150;
_position:absolute;
_top: expression(eval(document.documentElement.scrollTop+
(document.documentElement.clientHeight-this.offsetHeight)));
}
.change {
background-color: rgb( 223, 223, 223);
border: thin solid silver;
}
.error {
width: 100%;
background-color: red;
color: white;
}
.message {
border: thin solid red;
}
.minor-controls {
width: 10em;
float: right;
padding: 0.25em 2em;
color: white;
background:rgba(40,40,40,0.8);
font-size: 66%;
}
.minor-controls a {
float: right;
color: white;
}
.warn {
color: maroon;
}
.widget {
background-color: silver;
border: thin solid white;
margin-top: 0;
margin-bottom: 0;
width: 100%;
}
.wiki {
margin: 0;
}
form {
border: thin solid silver;
}
del {
color: red;
}
div.content, form, p, pre, h1, h2, h3, h4, h5 {
padding: 0.25em 5%;
}
dl, menu, ol, table, ul {
margin: 0.25em 5%;
}
input {
background-color: white;
}
input.action {
background-color: green;
}
input.action-dangerous {
color: white;
background-color: red;
}
input.required:after {
content: " \*";
color: red;
}
ins {
color: green;
}
label {
width: 20%;
min-width: 20em;
border-right: thin solid gray;
}
menu li {
display: inline;
}
menu li::before {
content: "|| ";
}
table {
border: 2px solid black;
border-collapse: collapse;
}
table.music-ruled tr:nth-child(odd) {
background-color: silver;
}
th, td {
text-align: left;
vertical-align: top;
padding: 0.15em 1.5em;
border: 1px solid gray;
}
th {
background-color: silver;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -3,19 +3,35 @@
<head>
<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="css/re-com.css">
<link rel="stylesheet" href="css/standard.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">
<title>Example swinging needle meter following re-com conventions.</title>
</head>
<body>
<div id="app"></div>
<div id="app">
<h1>
Please be patient...
</h1>
<p>
If you're seeing this, Javascript has not yet loaded. If you continue
to see this for more than a minute, it may be that something has broken.
</p>
</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>
<footer>
<div id="credits">
<div>
<img height="16" width="16" alt="ClojureScript" src="images/credits/cljs-icon.png"/> Powered by <a href="http://clojurescript.org">ClojureScript</a> ||
<img height="16" width="16" alt="GitHub" src="images/credits/github-logo-transparent.png"/>Find me/fork me on <a href="https://github.com/simon-brooke/swinging-needle-meter">Github</a> ||
<img height="16" width="16" alt="Free Software Foundation" src="images/credits/gnu.small.png"/>Licensed under the <a href="http://www.gnu.org/licenses/gpl-2.0.html">GNU General Public License version 2.0</a>
</div>
</div>
</footer>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -1,10 +1,9 @@
(ns swinging-needle-meter.swinging-needle-meter
(:require [clojure.string :as string]
[re-com.core :refer [h-box v-box box gap line label title slider checkbox p]]
[re-com.core :refer [box]]
[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]
[swinging-needle-meter.utils :refer [abs]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -27,7 +26,7 @@
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
;;;; USA.
;;;;
;;;; Copyright (C) 2014 Simon Brooke
;;;; Copyright (C) 2017 Simon Brooke
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -87,7 +86,6 @@
;; from the left end of the scale to right end, in degrees.
(def full-scale-deflection 140)
(defn deflection
"Return the linear deflection of a needle given this `value` on the
range `min-value`...`max-value`."
@ -97,7 +95,6 @@
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
@ -108,7 +105,6 @@
{: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
@ -121,7 +117,6 @@
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]
@ -130,7 +125,6 @@
(.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`."
@ -151,13 +145,6 @@
: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
"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]
@ -181,7 +168,6 @@
{: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)
cx (/ width 2)
cy (* height 0.90)
needle-length (* height 0.75)
@ -217,7 +203,7 @@
:y (/ height 2)
:width "100"
:id (str id "-current-value")
:class "snm-value"}[:tspan (str (as-label model) (if unit " ") unit)]]
:class "snm-value"}[:tspan (str (as-label model) (when unit " ") unit)]]
[:path {:class scale-class
:id (str id "-scale")
:d (describe-arc cx cy scale-radius
@ -237,7 +223,7 @@
: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 ")") }]
(if (> gradations 0)
(when (> gradations 0)
(apply vector (cons :g (map #(let
[value (+ min-value
(*

View file

@ -1,5 +1,5 @@
(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 gap title line label hyperlink-href align-style]]))
;;;; This file is mostly stolen wholesale from re-demo in the re-com package;
;;;; I claim no credit for it.

View file

@ -1,25 +1,40 @@
(ns swinging-needle-meter.views
(: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]]
[re-com.core :refer [h-box v-box box label title slider p single-dropdown]]
[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]
[swinging-needle-meter.utils :refer [abs]]))
[swinging-needle-meter.utils :refer [panel-title title2 args-table status-text]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; swinging-needle-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) 2017 Simon Brooke
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ------------------------------------------------------------------------------------
;; Demo: swinging-needle-meter
;; ------------------------------------------------------------------------------------
(defn swinging-needle-demo
[]
(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])]
(let [size @(rf/subscribe [:size])]
(fn
[]
[v-box
@ -32,7 +47,7 @@
:gap "10px"
:width "450px"
:children [[title2 "Notes"]
[status-text "Wildly experimental"]
[status-text "Stable"]
[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,
@ -54,7 +69,8 @@
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]]]
[:a {:name "parameters"}
[args-table swinging-needle-args-desc]]]]
[v-box
:gap "10px"
:children [[title2 "Demo"]
@ -148,12 +164,12 @@
:gap "10px"
:children [[box :align :start :child [:code ":size"]]
[slider
:model size
:model @(rf/subscribe [:size])
:min 25
:max 100
:width "200px"
:on-change #(rf/dispatch [:set-size %])]
[label :label size]]]
[label :label @(rf/subscribe [:size])]]]
]]]]]]]])))