1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-02-21 15:54:39 +01:00

Merge pull request #4022 from henningpohl/multiplot

Added functionality to plot multiple signals at the same time
This commit is contained in:
Federico Fissore 2015-11-18 14:28:05 +01:00
commit 846b8216c8
3 changed files with 109 additions and 34 deletions

View File

@ -23,6 +23,7 @@ import processing.app.helpers.CircularBuffer;
import processing.app.helpers.Ticks; import processing.app.helpers.Ticks;
import processing.app.legacy.PApplet; import processing.app.legacy.PApplet;
import java.util.ArrayList;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import java.awt.*; import java.awt.*;
@ -35,24 +36,73 @@ import static processing.app.I18n.tr;
public class SerialPlotter extends AbstractMonitor { public class SerialPlotter extends AbstractMonitor {
private final StringBuffer messageBuffer; private final StringBuffer messageBuffer;
private CircularBuffer buffer;
private JComboBox<String> serialRates; private JComboBox<String> serialRates;
private Serial serial; private Serial serial;
private int serialRate; private int serialRate;
private ArrayList<Graph> graphs;
private final static int BUFFER_CAPACITY = 500;
private static class Graph {
public CircularBuffer buffer;
private Color color;
public Graph(int id) {
buffer = new CircularBuffer(BUFFER_CAPACITY);
color = Theme.getColorCycleColor("plotting.graphcolor", id);
}
public void paint(Graphics2D g, float xstep, double minY,
double maxY, double rangeY, double height) {
g.setColor(color);
g.setStroke(new BasicStroke(1.0f));
for (int i = 0; i < buffer.size() - 1; ++i) {
g.drawLine(
(int) (i * xstep), (int) transformY(buffer.get(i), minY, rangeY, height),
(int) ((i + 1) * xstep), (int) transformY(buffer.get(i + 1), minY, rangeY, height)
);
}
}
private float transformY(double rawY, double minY, double rangeY, double height) {
return (float) (5 + (height - 10) * (1.0 - (rawY - minY) / rangeY));
}
}
private class GraphPanel extends JPanel { private class GraphPanel extends JPanel {
private double minY, maxY, rangeY; private double minY, maxY, rangeY;
private Rectangle bounds; private Rectangle bounds;
private int xOffset; private int xOffset;
private final Font font; private final Font font;
private final Color graphColor; private final Color bgColor;
public GraphPanel() { public GraphPanel() {
font = Theme.getFont("console.font"); font = Theme.getFont("console.font");
graphColor = Theme.getColor("header.bgcolor"); bgColor = Theme.getColor("plotting.bgcolor");
xOffset = 20; xOffset = 20;
} }
private Ticks computeBounds() {
minY = Double.POSITIVE_INFINITY;
maxY = Double.NEGATIVE_INFINITY;
for(Graph g : graphs) {
double bMin = g.buffer.min() / 2.0;
double bMax = g.buffer.max() * 2.0;
minY = bMin < minY ? bMin : minY;
maxY = bMax > maxY ? bMax : maxY;
}
Ticks ticks = new Ticks(minY, maxY, 3);
minY = Math.min(minY, ticks.getTick(0));
maxY = Math.max(maxY, ticks.getTick(ticks.getTickCount() - 1));
rangeY = maxY - minY;
minY -= 0.05 * rangeY;
maxY += 0.05 * rangeY;
rangeY = maxY - minY;
return ticks;
}
@Override @Override
public void paintComponent(Graphics g1) { public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D) g1; Graphics2D g = (Graphics2D) g1;
@ -61,20 +111,12 @@ public class SerialPlotter extends AbstractMonitor {
super.paintComponent(g); super.paintComponent(g);
bounds = g.getClipBounds(); bounds = g.getClipBounds();
setBackground(Color.WHITE); setBackground(bgColor);
if (buffer.isEmpty()) { if (graphs.isEmpty()) {
return; return;
} }
minY = buffer.min() / 2; Ticks ticks = computeBounds();
maxY = buffer.max() * 2;
Ticks ticks = new Ticks(minY, maxY, 3);
minY = Math.min(minY, ticks.getTick(0));
maxY = Math.max(maxY, ticks.getTick(ticks.getTickCount() - 1));
rangeY = maxY - minY;
minY -= 0.05 * rangeY;
maxY += 0.05 * rangeY;
rangeY = maxY - minY;
g.setStroke(new BasicStroke(1.0f)); g.setStroke(new BasicStroke(1.0f));
FontMetrics fm = g.getFontMetrics(); FontMetrics fm = g.getFontMetrics();
@ -92,19 +134,21 @@ public class SerialPlotter extends AbstractMonitor {
g.drawLine(bounds.x + xOffset, bounds.y + 5, bounds.x + xOffset, bounds.y + bounds.height - 10); g.drawLine(bounds.x + xOffset, bounds.y + 5, bounds.x + xOffset, bounds.y + bounds.height - 10);
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0)); g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
float xstep = (float) (bounds.width - xOffset) / (float) buffer.capacity(); float xstep = (float) (bounds.width - xOffset) / (float) BUFFER_CAPACITY;
int legendLength = graphs.size() * 10 + (graphs.size() - 1) * 3;
g.setColor(graphColor); for(int i = 0; i < graphs.size(); ++i) {
g.setStroke(new BasicStroke(0.75f)); graphs.get(i).paint(g, xstep, minY, maxY, rangeY, bounds.height);
if(graphs.size() > 1) {
for (int i = 0; i < buffer.size() - 1; ++i) { g.fillRect(bounds.width - (xOffset + legendLength + 10) + i * 13, 10, 10, 10);
g.drawLine( }
(int) (i * xstep), (int) transformY(buffer.get(i)),
(int) ((i + 1) * xstep), (int) transformY(buffer.get(i + 1))
);
} }
} }
private float transformY(double rawY) {
return (float) (5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
}
@Override @Override
public Dimension getMinimumSize() { public Dimension getMinimumSize() {
return new Dimension(200, 100); return new Dimension(200, 100);
@ -114,10 +158,6 @@ public class SerialPlotter extends AbstractMonitor {
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
return new Dimension(500, 250); return new Dimension(500, 250);
} }
private float transformY(double rawY) {
return (float) (5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
}
} }
public SerialPlotter(BoardPort port) { public SerialPlotter(BoardPort port) {
@ -140,12 +180,12 @@ public class SerialPlotter extends AbstractMonitor {
}); });
messageBuffer = new StringBuffer(); messageBuffer = new StringBuffer();
graphs = new ArrayList<Graph>();
} }
protected void onCreateWindow(Container mainPane) { protected void onCreateWindow(Container mainPane) {
mainPane.setLayout(new BorderLayout()); mainPane.setLayout(new BorderLayout());
buffer = new CircularBuffer(500);
GraphPanel graphPanel = new GraphPanel(); GraphPanel graphPanel = new GraphPanel();
mainPane.add(graphPanel, BorderLayout.CENTER); mainPane.add(graphPanel, BorderLayout.CENTER);
@ -182,14 +222,26 @@ public class SerialPlotter extends AbstractMonitor {
} }
String line = messageBuffer.substring(0, linebreak); String line = messageBuffer.substring(0, linebreak);
line = line.trim();
messageBuffer.delete(0, linebreak + 1); messageBuffer.delete(0, linebreak + 1);
try { line = line.trim();
double value = Double.valueOf(line); String[] parts = line.split("[, \t]+");
buffer.add(value); if(parts.length == 0) {
} catch (NumberFormatException e) { continue;
// ignore }
int validParts = 0;
for(int i = 0; i < parts.length; ++i) {
try {
double value = Double.valueOf(parts[i]);
if(i >= graphs.size()) {
graphs.add(new Graph(validParts));
}
graphs.get(validParts).buffer.add(value);
validParts++;
} catch (NumberFormatException e) {
// ignore
}
} }
} }

View File

@ -94,6 +94,19 @@ public class Theme {
set(key, String.valueOf(value)); set(key, String.valueOf(value));
} }
static public Color getColorCycleColor(String name, int i) {
int cycleSize = getInteger(name + ".size");
name = String.format("%s.%02d", name, i % cycleSize);
return PreferencesHelper.parseColor(get(name));
}
static public void setColorCycleColor(String name, int i, Color color) {
name = String.format("%s.%02d", name, i);
PreferencesHelper.putColor(table, name, color);
int cycleSize = getInteger(name + ".size");
setInteger(name + ".size", (i + 1) > cycleSize ? (i + 1) : cycleSize);
}
static public Color getColor(String name) { static public Color getColor(String name) {
return PreferencesHelper.parseColor(get(name)); return PreferencesHelper.parseColor(get(name));
} }

View File

@ -37,6 +37,16 @@ buttons.bgcolor = #006468
buttons.status.font = SansSerif,plain,12 buttons.status.font = SansSerif,plain,12
buttons.status.color = #ffffff buttons.status.color = #ffffff
# GUI - PLOTTING
# color cycle created via colorbrewer2.org
plotting.bgcolor = #ffffff
plotting.color = #ffffff
plotting.graphcolor.size = 4
plotting.graphcolor.00 = #2c7bb6
plotting.graphcolor.01 = #fdae61
plotting.graphcolor.02 = #d7191c
plotting.graphcolor.03 = #abd9e9
# GUI - LINESTATUS # GUI - LINESTATUS
linestatus.color = #ffffff linestatus.color = #ffffff
linestatus.bgcolor = #006468 linestatus.bgcolor = #006468