Compare commits
No commits in common. "master" and "develop" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
||||||
/*.log
|
/*.log
|
||||||
/target
|
/target
|
||||||
/*-init.clj
|
/*-init.clj
|
||||||
|
/resources/public/js/compiled
|
||||||
|
out
|
||||||
|
|
||||||
resources/public/vendor/*
|
resources/public/vendor/*
|
||||||
|
|
64
README.md
64
README.md
|
@ -6,10 +6,6 @@ Works well in Chrome and Firefox; not tested in Internet Exploder.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Demo site
|
|
||||||
|
|
||||||
There's a demo site [here](https://simon-brooke.github.io/swinging-needle-meter/resources/public/index.html).
|
|
||||||
|
|
||||||
## Intended uses
|
## Intended uses
|
||||||
|
|
||||||
This is a component for a console, typically one controlling a technical or scientific instrument. It is
|
This is a component for a console, typically one controlling a technical or scientific instrument. It is
|
||||||
|
@ -20,63 +16,6 @@ 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).
|
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
|
## Development Mode
|
||||||
|
|
||||||
### Run application:
|
### Run application:
|
||||||
|
@ -92,6 +31,7 @@ Wait a bit, then browse to [http://localhost:3449](http://localhost:3449).
|
||||||
|
|
||||||
## Production Build
|
## Production Build
|
||||||
|
|
||||||
|
|
||||||
To compile clojurescript to javascript:
|
To compile clojurescript to javascript:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -102,5 +42,5 @@ lein cljsbuild once min
|
||||||
## License
|
## License
|
||||||
Copyright © 2017 Simon Brooke. Licensed under the GNU General Public 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
|
version 2.0 or (at your option) any later version. If you wish to incorporate
|
||||||
parts of this into another open source project which uses a less restrictive
|
parts of Smeagol into another open source project which uses a less restrictive
|
||||||
license, please contact me; I'm open to dual licensing it.
|
license, please contact me; I'm open to dual licensing it.
|
||||||
|
|
24
index.html
24
index.html
|
@ -1,24 +0,0 @@
|
||||||
<!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>
|
|
|
@ -1,4 +1,4 @@
|
||||||
(defproject swinging-needle-meter "1.0.3"
|
(defproject swinging-needle-meter "1.0.0"
|
||||||
: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"]
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
/* The following style addresses: http://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexbox */
|
/* The following style addresses: http://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexbox */
|
||||||
* {
|
* {
|
||||||
min-height: 0px;
|
min-height: 0px;
|
||||||
min-width: 0px;
|
min-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ html, body {
|
||||||
letter-spacing: 0.002em;
|
letter-spacing: 0.002em;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px 5%;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,282 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
Before Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB |
|
@ -3,35 +3,19 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<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="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/re-com.css">
|
||||||
<link rel="stylesheet" href="css/standard.css">
|
|
||||||
<link rel="stylesheet" href="css/swinging-needle-meter.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: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">
|
<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>
|
<title>Example swinging needle meter following re-com conventions.</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app"></div>
|
||||||
<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="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>
|
||||||
<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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,10 @@
|
||||||
(ns swinging-needle-meter.swinging-needle-meter
|
(ns swinging-needle-meter.swinging-needle-meter
|
||||||
(:require [clojure.string :as string]
|
(:require [clojure.string :as string]
|
||||||
[re-com.core :refer [box]]
|
[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.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]
|
||||||
[swinging-needle-meter.utils :refer [abs]]))
|
[swinging-needle-meter.utils :refer [abs]]))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||||
;;;; USA.
|
;;;; USA.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; Copyright (C) 2017 Simon Brooke
|
;;;; Copyright (C) 2014 Simon Brooke
|
||||||
;;;;
|
;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@
|
||||||
;; 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)
|
||||||
|
|
||||||
|
|
||||||
(defn deflection
|
(defn deflection
|
||||||
"Return the linear 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`."
|
||||||
|
@ -95,6 +97,7 @@
|
||||||
limited (min (max (+ zero-offset (/ value range)) 0) 1)]
|
limited (min (max (+ zero-offset (/ value range)) 0) 1)]
|
||||||
(* (- 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
|
||||||
|
@ -105,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
|
||||||
|
@ -117,6 +121,7 @@
|
||||||
sweep (if (> end-angle start-angle) 1 0)]
|
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)])))
|
(string/join " " ["M" (:x start) (:y start) "A" radius radius 0 large-arc? sweep (:x end) (:y end)])))
|
||||||
|
|
||||||
|
|
||||||
(defn as-label
|
(defn as-label
|
||||||
"If this arg is a floating point number, format it to a reasonable width; else return it."
|
"If this arg is a floating point number, format it to a reasonable width; else return it."
|
||||||
[arg]
|
[arg]
|
||||||
|
@ -125,6 +130,7 @@
|
||||||
(.toFixed arg 2)
|
(.toFixed arg 2)
|
||||||
arg))
|
arg))
|
||||||
|
|
||||||
|
|
||||||
(defn gradation
|
(defn gradation
|
||||||
"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`."
|
||||||
|
@ -145,6 +151,13 @@
|
||||||
:x cx
|
:x cx
|
||||||
:y (- cy min-radius)} (as-label label)]])
|
: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]
|
||||||
|
@ -168,6 +181,7 @@
|
||||||
{:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]}
|
{:pre [(validate-args-macro swinging-needle-args-desc args "swinging-needle")]}
|
||||||
(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)
|
||||||
cx (/ width 2)
|
cx (/ width 2)
|
||||||
cy (* height 0.90)
|
cy (* height 0.90)
|
||||||
needle-length (* height 0.75)
|
needle-length (* height 0.75)
|
||||||
|
@ -203,7 +217,7 @@
|
||||||
:y (/ height 2)
|
: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) (when 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 cx cy scale-radius
|
:d (describe-arc cx cy scale-radius
|
||||||
|
@ -223,7 +237,7 @@
|
||||||
:id (str id "-needle")
|
:id (str id "-needle")
|
||||||
:d (str "M " cx "," (- cy needle-length) " " cx "," cy) ;; "M cx,20 cx,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 ")") }]
|
:transform (str "rotate( " (deflection model min-value max-value) "," cx "," cy ")") }]
|
||||||
(when (> gradations 0)
|
(if (> gradations 0)
|
||||||
(apply vector (cons :g (map #(let
|
(apply vector (cons :g (map #(let
|
||||||
[value (+ min-value
|
[value (+ min-value
|
||||||
(*
|
(*
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
(ns swinging-needle-meter.utils
|
(ns swinging-needle-meter.utils
|
||||||
(:require [re-com.core :refer [h-box v-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 mostly 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.
|
||||||
|
|
|
@ -1,40 +1,25 @@
|
||||||
(ns swinging-needle-meter.views
|
(ns swinging-needle-meter.views
|
||||||
(:require [re-frame.core :as rf]
|
(:require [re-frame.core :as rf]
|
||||||
[re-com.core :refer [h-box v-box box label title slider p single-dropdown]]
|
[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 status-text]]))
|
[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: 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
|
;; Demo: swinging-needle-meter
|
||||||
;; ------------------------------------------------------------------------------------
|
;; ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
(defn swinging-needle-demo
|
(defn swinging-needle-demo
|
||||||
[]
|
[]
|
||||||
(let [size @(rf/subscribe [:size])]
|
(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
|
(fn
|
||||||
[]
|
[]
|
||||||
[v-box
|
[v-box
|
||||||
|
@ -47,7 +32,7 @@
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:width "450px"
|
:width "450px"
|
||||||
:children [[title2 "Notes"]
|
:children [[title2 "Notes"]
|
||||||
[status-text "Stable"]
|
[status-text "Wildly experimental"]
|
||||||
[p "An SVG swinging needle meter intended to be useful in dashboards."]
|
[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 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,
|
[p "Note that if the value of model is lower then min-value or greater than max-value,
|
||||||
|
@ -69,8 +54,7 @@
|
||||||
value, the class target-class will be set on the meter to indicate an on-target status. The setpoint value, like the model value,
|
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."]
|
may change dynamically at run-time."]
|
||||||
|
|
||||||
[:a {:name "parameters"}
|
[args-table swinging-needle-args-desc]]]
|
||||||
[args-table swinging-needle-args-desc]]]]
|
|
||||||
[v-box
|
[v-box
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[title2 "Demo"]
|
:children [[title2 "Demo"]
|
||||||
|
@ -164,12 +148,12 @@
|
||||||
:gap "10px"
|
:gap "10px"
|
||||||
:children [[box :align :start :child [:code ":size"]]
|
:children [[box :align :start :child [:code ":size"]]
|
||||||
[slider
|
[slider
|
||||||
:model @(rf/subscribe [:size])
|
:model size
|
||||||
:min 25
|
:min 25
|
||||||
:max 100
|
:max 100
|
||||||
:width "200px"
|
:width "200px"
|
||||||
:on-change #(rf/dispatch [:set-size %])]
|
:on-change #(rf/dispatch [:set-size %])]
|
||||||
[label :label @(rf/subscribe [:size])]]]
|
[label :label size]]]
|
||||||
]]]]]]]])))
|
]]]]]]]])))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue