diff options
| author | s-ol <s+removethis@s-ol.nu> | 2024-03-13 22:20:02 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2024-03-13 22:20:02 +0000 |
| commit | 29bf3830fece4b4f175bcbb99b5442728ad8bad0 (patch) | |
| tree | e2a107a16546cba5e794c82942bf4883dfe52343 /web/plot.js | |
| parent | web: editable setpoint curve (diff) | |
| download | t937-serial-29bf3830fece4b4f175bcbb99b5442728ad8bad0.tar.gz t937-serial-29bf3830fece4b4f175bcbb99b5442728ad8bad0.zip | |
web: add crosshair
Diffstat (limited to 'web/plot.js')
| -rw-r--r-- | web/plot.js | 83 |
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(); |
