diff options
Diffstat (limited to 'web/plot.js')
| -rw-r--r-- | web/plot.js | 84 |
1 files changed, 74 insertions, 10 deletions
diff --git a/web/plot.js b/web/plot.js index ae826d5..4c2320f 100644 --- a/web/plot.js +++ b/web/plot.js @@ -22,31 +22,77 @@ const sec = d3.format('02'); const min = d3.format(' '); const minsec = v => `${min(Math.floor(v / 60))}:${sec(v % 60)}`; -export const addPlot = (name) => { +export const addPlot = (name, color, data=[], interactive=false) => { const path = svg.append('path') + // .attr('cursor', 'copy') .attr('fill', 'none') - .attr('stroke', 'black') + .attr('stroke', color) .attr('stroke-width', '2'); + const points = interactive && ( + svg.append('g') + .attr('cursor', 'grab') + ); + PLOTS[name] = { - data: [], + color, + data, + points, path, }; return PLOTS[name]; }; - -export const update = () => { +export const update = (transition=true) => { const times = d3.merge(Object.values(PLOTS).map(p => p.data.map(d => d[0]))); const temps = d3.merge(Object.values(PLOTS).map(p => p.data.map(d => d[1]))); x.domain([0, d3.max([...times, 120])]).nice(30); y.domain([0, d3.max([...temps, 200])]).nice(25); - xa.transition().call(d3.axisBottom(x).tickFormat(minsec)) - ya.transition().call(d3.axisLeft(y)) - for (const { path, data } of Object.values(PLOTS)) { - path.transition().attr('d', line(data)); + const dur = transition ? null : 0; + + xa.transition().duration(dur).call(d3.axisBottom(x).tickFormat(minsec)) + ya.transition().duration(dur).call(d3.axisLeft(y)) + for (const { path, points, data, color } of Object.values(PLOTS)) { + path.transition().duration(dur).attr('d', line(data)); + points && points.selectAll('circle') + .data(data) + .join('circle') + .attr('fill', color) + .attr('r', 5) + .attr('cx', (d) => x(d[0])) + .attr('cy', (d) => y(d[1])) + .on('dblclick', function (e, d) { + e.stopPropagation(); + + const i = data.indexOf(d); + data.splice(i, 1); + update(false); + }) + .call( + d3.drag() + .on('start', () => points.attr('cursor', 'grabbing')) + .on('drag', function (e, d) { + const [time, temp] = [x.invert(e.x), y.invert(e.y)]; + + const ci = data.indexOf(d); + if (data[ci-1] && data[ci-1][0] > time) { + data.splice(ci-1, 0, ...data.splice(ci, 1)); + } else if (data[ci+1] && data[ci+1][0] < time) { + data.splice(ci+1, 0, ...data.splice(ci, 1)); + } + + d[0] = time; + d[1] = temp; + + d3.select(this) + .attr('cx', (d) => x(d[0])) + .attr('cy', (d) => y(d[1])); + update(false); + }) + .on('end', () => points.attr('cursor', 'grab')) + ); } }; @@ -61,8 +107,11 @@ const onresize = () => { y.range([height - margin, margin]); xa.transition().call(d3.axisBottom(x).tickFormat(minsec)) ya.transition().call(d3.axisLeft(y)) - for (const { path, data } of Object.values(PLOTS)) { + for (const { path, data, points } of Object.values(PLOTS)) { path.transition().attr('d', line(data)); + points && points.transition().selectAll('circle') + .attr('cx', (d) => x(d[0])) + .attr('cy', (d) => y(d[1])); } xa.attr('transform', `translate(0, ${height - margin})`); @@ -70,5 +119,20 @@ 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); +}); + onresize(); update(); |
