Skip to content

Commit 846b821

Browse files
committed
Merge pull request #4022 from henningpohl/multiplot
Added functionality to plot multiple signals at the same time
2 parents 8a3cca7 + 7d78a9c commit 846b821

File tree

3 files changed

+110
-35
lines changed

3 files changed

+110
-35
lines changed

Diff for: app/src/processing/app/SerialPlotter.java

+87-35
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import processing.app.helpers.Ticks;
2424
import processing.app.legacy.PApplet;
2525

26+
import java.util.ArrayList;
2627
import javax.swing.*;
2728
import javax.swing.border.EmptyBorder;
2829
import java.awt.*;
@@ -35,24 +36,73 @@
3536
public class SerialPlotter extends AbstractMonitor {
3637

3738
private final StringBuffer messageBuffer;
38-
private CircularBuffer buffer;
3939
private JComboBox<String> serialRates;
4040
private Serial serial;
4141
private int serialRate;
4242

43+
private ArrayList<Graph> graphs;
44+
private final static int BUFFER_CAPACITY = 500;
45+
46+
private static class Graph {
47+
public CircularBuffer buffer;
48+
private Color color;
49+
50+
public Graph(int id) {
51+
buffer = new CircularBuffer(BUFFER_CAPACITY);
52+
color = Theme.getColorCycleColor("plotting.graphcolor", id);
53+
}
54+
55+
public void paint(Graphics2D g, float xstep, double minY,
56+
double maxY, double rangeY, double height) {
57+
g.setColor(color);
58+
g.setStroke(new BasicStroke(1.0f));
59+
60+
for (int i = 0; i < buffer.size() - 1; ++i) {
61+
g.drawLine(
62+
(int) (i * xstep), (int) transformY(buffer.get(i), minY, rangeY, height),
63+
(int) ((i + 1) * xstep), (int) transformY(buffer.get(i + 1), minY, rangeY, height)
64+
);
65+
}
66+
}
67+
68+
private float transformY(double rawY, double minY, double rangeY, double height) {
69+
return (float) (5 + (height - 10) * (1.0 - (rawY - minY) / rangeY));
70+
}
71+
}
72+
4373
private class GraphPanel extends JPanel {
4474
private double minY, maxY, rangeY;
4575
private Rectangle bounds;
4676
private int xOffset;
4777
private final Font font;
48-
private final Color graphColor;
78+
private final Color bgColor;
4979

5080
public GraphPanel() {
5181
font = Theme.getFont("console.font");
52-
graphColor = Theme.getColor("header.bgcolor");
82+
bgColor = Theme.getColor("plotting.bgcolor");
5383
xOffset = 20;
5484
}
5585

86+
private Ticks computeBounds() {
87+
minY = Double.POSITIVE_INFINITY;
88+
maxY = Double.NEGATIVE_INFINITY;
89+
for(Graph g : graphs) {
90+
double bMin = g.buffer.min() / 2.0;
91+
double bMax = g.buffer.max() * 2.0;
92+
minY = bMin < minY ? bMin : minY;
93+
maxY = bMax > maxY ? bMax : maxY;
94+
}
95+
96+
Ticks ticks = new Ticks(minY, maxY, 3);
97+
minY = Math.min(minY, ticks.getTick(0));
98+
maxY = Math.max(maxY, ticks.getTick(ticks.getTickCount() - 1));
99+
rangeY = maxY - minY;
100+
minY -= 0.05 * rangeY;
101+
maxY += 0.05 * rangeY;
102+
rangeY = maxY - minY;
103+
return ticks;
104+
}
105+
56106
@Override
57107
public void paintComponent(Graphics g1) {
58108
Graphics2D g = (Graphics2D) g1;
@@ -61,20 +111,12 @@ public void paintComponent(Graphics g1) {
61111
super.paintComponent(g);
62112

63113
bounds = g.getClipBounds();
64-
setBackground(Color.WHITE);
65-
if (buffer.isEmpty()) {
114+
setBackground(bgColor);
115+
if (graphs.isEmpty()) {
66116
return;
67117
}
68118

69-
minY = buffer.min() / 2;
70-
maxY = buffer.max() * 2;
71-
Ticks ticks = new Ticks(minY, maxY, 3);
72-
minY = Math.min(minY, ticks.getTick(0));
73-
maxY = Math.max(maxY, ticks.getTick(ticks.getTickCount() - 1));
74-
rangeY = maxY - minY;
75-
minY -= 0.05 * rangeY;
76-
maxY += 0.05 * rangeY;
77-
rangeY = maxY - minY;
119+
Ticks ticks = computeBounds();
78120

79121
g.setStroke(new BasicStroke(1.0f));
80122
FontMetrics fm = g.getFontMetrics();
@@ -92,19 +134,21 @@ public void paintComponent(Graphics g1) {
92134
g.drawLine(bounds.x + xOffset, bounds.y + 5, bounds.x + xOffset, bounds.y + bounds.height - 10);
93135

94136
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
95-
float xstep = (float) (bounds.width - xOffset) / (float) buffer.capacity();
96-
97-
g.setColor(graphColor);
98-
g.setStroke(new BasicStroke(0.75f));
99-
100-
for (int i = 0; i < buffer.size() - 1; ++i) {
101-
g.drawLine(
102-
(int) (i * xstep), (int) transformY(buffer.get(i)),
103-
(int) ((i + 1) * xstep), (int) transformY(buffer.get(i + 1))
104-
);
137+
float xstep = (float) (bounds.width - xOffset) / (float) BUFFER_CAPACITY;
138+
int legendLength = graphs.size() * 10 + (graphs.size() - 1) * 3;
139+
140+
for(int i = 0; i < graphs.size(); ++i) {
141+
graphs.get(i).paint(g, xstep, minY, maxY, rangeY, bounds.height);
142+
if(graphs.size() > 1) {
143+
g.fillRect(bounds.width - (xOffset + legendLength + 10) + i * 13, 10, 10, 10);
144+
}
105145
}
106146
}
107147

148+
private float transformY(double rawY) {
149+
return (float) (5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
150+
}
151+
108152
@Override
109153
public Dimension getMinimumSize() {
110154
return new Dimension(200, 100);
@@ -114,10 +158,6 @@ public Dimension getMinimumSize() {
114158
public Dimension getPreferredSize() {
115159
return new Dimension(500, 250);
116160
}
117-
118-
private float transformY(double rawY) {
119-
return (float) (5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
120-
}
121161
}
122162

123163
public SerialPlotter(BoardPort port) {
@@ -140,12 +180,12 @@ public SerialPlotter(BoardPort port) {
140180
});
141181

142182
messageBuffer = new StringBuffer();
183+
graphs = new ArrayList<Graph>();
143184
}
144185

145186
protected void onCreateWindow(Container mainPane) {
146187
mainPane.setLayout(new BorderLayout());
147188

148-
buffer = new CircularBuffer(500);
149189
GraphPanel graphPanel = new GraphPanel();
150190

151191
mainPane.add(graphPanel, BorderLayout.CENTER);
@@ -182,14 +222,26 @@ public void message(final String s) {
182222
}
183223

184224
String line = messageBuffer.substring(0, linebreak);
185-
line = line.trim();
186225
messageBuffer.delete(0, linebreak + 1);
187226

188-
try {
189-
double value = Double.valueOf(line);
190-
buffer.add(value);
191-
} catch (NumberFormatException e) {
192-
// ignore
227+
line = line.trim();
228+
String[] parts = line.split("[, \t]+");
229+
if(parts.length == 0) {
230+
continue;
231+
}
232+
233+
int validParts = 0;
234+
for(int i = 0; i < parts.length; ++i) {
235+
try {
236+
double value = Double.valueOf(parts[i]);
237+
if(i >= graphs.size()) {
238+
graphs.add(new Graph(validParts));
239+
}
240+
graphs.get(validParts).buffer.add(value);
241+
validParts++;
242+
} catch (NumberFormatException e) {
243+
// ignore
244+
}
193245
}
194246
}
195247

Diff for: app/src/processing/app/Theme.java

+13
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@ static public void setInteger(String key, int value) {
9494
set(key, String.valueOf(value));
9595
}
9696

97+
static public Color getColorCycleColor(String name, int i) {
98+
int cycleSize = getInteger(name + ".size");
99+
name = String.format("%s.%02d", name, i % cycleSize);
100+
return PreferencesHelper.parseColor(get(name));
101+
}
102+
103+
static public void setColorCycleColor(String name, int i, Color color) {
104+
name = String.format("%s.%02d", name, i);
105+
PreferencesHelper.putColor(table, name, color);
106+
int cycleSize = getInteger(name + ".size");
107+
setInteger(name + ".size", (i + 1) > cycleSize ? (i + 1) : cycleSize);
108+
}
109+
97110
static public Color getColor(String name) {
98111
return PreferencesHelper.parseColor(get(name));
99112
}

Diff for: build/shared/lib/theme/theme.txt

+10
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ buttons.bgcolor = #006468
3737
buttons.status.font = SansSerif,plain,12
3838
buttons.status.color = #ffffff
3939

40+
# GUI - PLOTTING
41+
# color cycle created via colorbrewer2.org
42+
plotting.bgcolor = #ffffff
43+
plotting.color = #ffffff
44+
plotting.graphcolor.size = 4
45+
plotting.graphcolor.00 = #2c7bb6
46+
plotting.graphcolor.01 = #fdae61
47+
plotting.graphcolor.02 = #d7191c
48+
plotting.graphcolor.03 = #abd9e9
49+
4050
# GUI - LINESTATUS
4151
linestatus.color = #ffffff
4252
linestatus.bgcolor = #006468

0 commit comments

Comments
 (0)