aboutsummaryrefslogtreecommitdiffstats
path: root/web/plot.js
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2024-03-13 22:20:02 +0000
committers-ol <s+removethis@s-ol.nu>2024-03-13 22:20:02 +0000
commit29bf3830fece4b4f175bcbb99b5442728ad8bad0 (patch)
treee2a107a16546cba5e794c82942bf4883dfe52343 /web/plot.js
parentweb: editable setpoint curve (diff)
downloadt937-serial-29bf3830fece4b4f175bcbb99b5442728ad8bad0.tar.gz
t937-serial-29bf3830fece4b4f175bcbb99b5442728ad8bad0.zip
web: add crosshair
Diffstat (limited to 'web/plot.js')
-rw-r--r--web/plot.js83
1 files changed, 63 insertions, 20 deletions
diff --git a/web/plot.js b/web/plot.js
index 4c2320f..9095ec3 100644
--- a/web/plot.js
+++ b/web/plot.js
@@ -20,7 +20,8 @@ const PLOTS = {};
const sec = d3.format('02');
const min = d3.format(' ');
-const minsec = v => `${min(Math.floor(v / 60))}:${sec(v % 60)}`;
+const minsec = v => `${min(Math.floor(v / 60))}:${sec(Math.floor(v % 60))}`;
+const degc = v => `${Math.floor(v)}°C`;
export const addPlot = (name, color, data=[], interactive=false) => {
const path = svg.append('path')
@@ -52,8 +53,8 @@ export const update = (transition=true) => {
const dur = transition ? null : 0;
- xa.transition().duration(dur).call(d3.axisBottom(x).tickFormat(minsec))
- ya.transition().duration(dur).call(d3.axisLeft(y))
+ xa.transition().duration(dur).call(d3.axisBottom(x).tickFormat(minsec));
+ ya.transition().duration(dur).call(d3.axisLeft(y).tickFormat(degc));
for (const { path, points, data, color } of Object.values(PLOTS)) {
path.transition().duration(dur).attr('d', line(data));
points && points.selectAll('circle')
@@ -65,6 +66,7 @@ export const update = (transition=true) => {
.attr('cy', (d) => y(d[1]))
.on('dblclick', function (e, d) {
e.stopPropagation();
+ if (d[0] === 0) return;
const i = data.indexOf(d);
data.splice(i, 1);
@@ -83,7 +85,7 @@ export const update = (transition=true) => {
data.splice(ci+1, 0, ...data.splice(ci, 1));
}
- d[0] = time;
+ if (d[0] !== 0) d[0] = time;
d[1] = temp;
d3.select(this)
@@ -105,8 +107,8 @@ const onresize = () => {
x.range([margin, width - margin]);
y.range([height - margin, margin]);
- xa.transition().call(d3.axisBottom(x).tickFormat(minsec))
- ya.transition().call(d3.axisLeft(y))
+ xa.transition().call(d3.axisBottom(x).tickFormat(minsec));
+ ya.transition().call(d3.axisLeft(y).tickFormat(degc));
for (const { path, data, points } of Object.values(PLOTS)) {
path.transition().attr('d', line(data));
points && points.transition().selectAll('circle')
@@ -119,20 +121,61 @@ const onresize = () => {
};
window.addEventListener('resize', onresize);
-svg.on('dblclick', (e) => {
- const [px, py] = d3.pointer(e);
- const [time, temp] = [x.invert(px), y.invert(py)];
-
- const current = PLOTS.setpoint;
-
- const ni = current.data.findLastIndex(([tt, tp]) => tt <= time);
- if (current.data[ni][0] === time) {
- current.data[ni][1] = temp;
- } else {
- current.data.splice(ni + 1, 0, [time, temp]);
- }
- update(false);
-});
+const crosshair = svg.append('g')
+ .attr('display', 'none')
+ .attr('color', '#00000080')
+ .attr('font-size', 10)
+ .attr('font-family', 'sans-serif')
+ .attr('stroke-width', 0.5);
+crosshair.append('text')
+ .attr('id', 'ctextx')
+ .attr('y', margin)
+ .attr('dx', '0.32em')
+ .attr('dy', '1em')
+ .attr('fill', 'currentColor');
+crosshair.append('text')
+ .attr('id', 'ctexty')
+ .attr('x', margin)
+ .attr('dx', '0.32em')
+ .attr('dy', '-0.32em')
+ .attr('fill', 'currentColor');;
+crosshair.append('line')
+ .attr('id', 'clinex')
+ .attr('stroke', 'currentColor');
+crosshair.append('line')
+ .attr('id', 'cliney')
+ .attr('stroke', 'currentColor');
+
+svg
+ .on('mousemove', (e) => {
+ const [px, py] = d3.pointer(e);
+ const [time, temp] = [x.invert(px), y.invert(py)];
+
+ const width = svg.node().clientWidth;
+ const height = svg.node().clientHeight;
+ const inRange = margin < px && px < width - margin &&
+ margin < py && py < height - margin;
+
+ crosshair.attr('display', inRange ? null : 'none').lower();
+ crosshair.select('#ctextx').text(minsec(time)).attr('x', px);
+ crosshair.select('#ctexty').text(degc(temp)).attr('y', py);
+ crosshair.select('#clinex').attr('x1', px).attr('x2', px).attr('y1', margin).attr('y2', height - margin);
+ crosshair.select('#cliney').attr('y1', py).attr('y2', py).attr('x1', margin).attr('x2', width - margin);
+ })
+ .on('dblclick', (e) => {
+ const [px, py] = d3.pointer(e);
+ const [time, temp] = [x.invert(px), y.invert(py)];
+
+ const current = PLOTS.setpoint;
+
+ const ni = current.data.findLastIndex(([tt, tp]) => tt <= time);
+ if (current.data[ni][0] === time) {
+ current.data[ni][1] = temp;
+ } else {
+ current.data.splice(ni + 1, 0, [time, temp]);
+ }
+ update(false);
+ });
onresize();
update();