Code cleanup

SerialPlotter now uses min/2 and max*2 instead of plain min and max
Ticks works also when min == max
Fixes #3767
This commit is contained in:
Federico Fissore 2015-09-08 16:25:09 +02:00
parent a4a7e3ec96
commit 4cb72ceb9b
3 changed files with 121 additions and 131 deletions

View File

@ -19,23 +19,24 @@
package processing.app;
import cc.arduino.packages.BoardPort;
import processing.app.helpers.CircularBuffer;
import processing.app.helpers.Ticks;
import processing.app.legacy.PApplet;
import processing.app.helpers.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import static processing.app.I18n.tr;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
public class SerialPlotter extends AbstractMonitor {
private StringBuffer messageBuffer;
private CircularBuffer buffer;
private GraphPanel graphPanel;
private JComboBox serialRates;
private final StringBuffer messageBuffer;
private CircularBuffer buffer;
private JComboBox<String> serialRates;
private Serial serial;
private int serialRate;
@ -43,30 +44,30 @@ public class SerialPlotter extends AbstractMonitor {
private double minY, maxY, rangeY;
private Rectangle bounds;
private int xOffset;
private Font font;
private Color graphColor;
private final Font font;
private final Color graphColor;
public GraphPanel() {
font = Theme.getFont("console.font");
graphColor = Theme.getColor("header.bgcolor");
xOffset = 20;
}
@Override
public void paintComponent(Graphics g1) {
Graphics2D g = (Graphics2D)g1;
Graphics2D g = (Graphics2D) g1;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(font);
super.paintComponent(g);
bounds = g.getClipBounds();
setBackground(Color.WHITE);
if(buffer.isEmpty()) {
if (buffer.isEmpty()) {
return;
}
minY = buffer.min();
maxY = buffer.max();
minY = buffer.min() / 2;
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));
@ -77,45 +78,45 @@ public class SerialPlotter extends AbstractMonitor {
g.setStroke(new BasicStroke(1.0f));
FontMetrics fm = g.getFontMetrics();
for(int i = 0; i < ticks.getTickCount(); ++i) {
for (int i = 0; i < ticks.getTickCount(); ++i) {
double tick = ticks.getTick(i);
Rectangle2D fRect = fm.getStringBounds(String.valueOf(tick), g);
xOffset = Math.max(xOffset, (int)fRect.getWidth() + 15);
xOffset = Math.max(xOffset, (int) fRect.getWidth() + 15);
// draw tick
g.drawLine(xOffset - 5, (int)transformY(tick), xOffset + 2, (int)transformY(tick));
g.drawLine(xOffset - 5, (int) transformY(tick), xOffset + 2, (int) transformY(tick));
// draw tick label
g.drawString(String.valueOf(tick), xOffset - (int)fRect.getWidth() - 10, transformY(tick) - (float)fRect.getHeight() * 0.5f + fm.getAscent());
g.drawString(String.valueOf(tick), xOffset - (int) fRect.getWidth() - 10, transformY(tick) - (float) fRect.getHeight() * 0.5f + fm.getAscent());
}
g.drawLine(bounds.x + xOffset, bounds.y + 5, bounds.x + xOffset, bounds.y + bounds.height - 10);
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
float xstep = (float)(bounds.width - xOffset) / (float)buffer.capacity();
g.setTransform(AffineTransform.getTranslateInstance(xOffset, 0));
float xstep = (float) (bounds.width - xOffset) / (float) buffer.capacity();
g.setColor(graphColor);
g.setStroke(new BasicStroke(0.75f));
for(int i = 0; i < buffer.size() - 1; ++i) {
for (int i = 0; i < buffer.size() - 1; ++i) {
g.drawLine(
(int)(i * xstep), (int)transformY(buffer.get(i)),
(int)((i + 1) * xstep), (int)transformY(buffer.get(i + 1))
(int) (i * xstep), (int) transformY(buffer.get(i)),
(int) ((i + 1) * xstep), (int) transformY(buffer.get(i + 1))
);
}
}
@Override
public Dimension getMinimumSize() {
return new Dimension(200, 100);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 250);
}
private float transformY(double rawY) {
return (float)(5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
return (float) (5 + (bounds.height - 10) * (1.0 - (rawY - minY) / rangeY));
}
}
@ -124,93 +125,80 @@ public class SerialPlotter extends AbstractMonitor {
serialRate = PreferencesData.getInteger("serial.debug_rate");
serialRates.setSelectedItem(serialRate + " " + tr("baud"));
onSerialRateChange(new ActionListener() {
public void actionPerformed(ActionEvent event) {
String wholeString = (String) serialRates.getSelectedItem();
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
serialRate = Integer.parseInt(rateString);
PreferencesData.set("serial.debug_rate", rateString);
try {
close();
Thread.sleep(100); // Wait for serial port to properly close
open();
} catch (InterruptedException e) {
// noop
} catch (Exception e) {
System.err.println(e);
}
onSerialRateChange(event -> {
String wholeString = (String) serialRates.getSelectedItem();
String rateString = wholeString.substring(0, wholeString.indexOf(' '));
serialRate = Integer.parseInt(rateString);
PreferencesData.set("serial.debug_rate", rateString);
try {
close();
Thread.sleep(100); // Wait for serial port to properly close
open();
} catch (Exception e) {
// ignore
}
});
messageBuffer = new StringBuffer();
}
protected void onCreateWindow(Container mainPane) {
mainPane.setLayout(new BorderLayout());
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize());
buffer = new CircularBuffer(500);
graphPanel = new GraphPanel();
GraphPanel graphPanel = new GraphPanel();
mainPane.add(graphPanel, BorderLayout.CENTER);
JPanel pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
pane.setBorder(new EmptyBorder(4, 4, 4, 4));
serialRates = new JComboBox();
for (int i = 0; i < serialRateStrings.length; i++)
serialRates.addItem(serialRateStrings[i] + " " + tr("baud"));
serialRates = new JComboBox<>();
for (String serialRateString : serialRateStrings) serialRates.addItem(serialRateString + " " + tr("baud"));
serialRates.setMaximumSize(serialRates.getMinimumSize());
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(serialRates);
mainPane.add(pane, BorderLayout.SOUTH);
}
protected void onEnableWindow(boolean enable)
{
serialRates.setEnabled(enable);
protected void onEnableWindow(boolean enable) {
serialRates.setEnabled(enable);
}
public void onSerialRateChange(ActionListener listener) {
private void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}
public void message(final String s) {
messageBuffer.append(s);
while(true) {
while (true) {
int linebreak = messageBuffer.indexOf("\n");
if(linebreak == -1) {
if (linebreak == -1) {
break;
}
String line = messageBuffer.substring(0, linebreak);
line = line.trim();
messageBuffer.delete(0, linebreak + 1);
try {
double value = Double.valueOf(line);
buffer.add(value);
} catch(NumberFormatException e) {
continue; // ignore lines that can't be cast to a number
} catch (NumberFormatException e) {
// ignore
}
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SerialPlotter.this.repaint();
}});
SwingUtilities.invokeLater(SerialPlotter.this::repaint);
}
public void open() throws Exception {
super.open();
if (serial != null) return;
serial = new Serial(getBoardPort().getAddress(), serialRate) {

View File

@ -3,77 +3,74 @@ package processing.app.helpers;
import java.util.NoSuchElementException;
public class CircularBuffer {
private double[] elements;
private final double[] elements;
private int start = -1;
private int end = -1;
private int capacity;
private final int capacity;
public void add(double num) {
end = (end + 1) % capacity;
elements[end] = num;
if(start == end || start == -1) {
if (start == end || start == -1) {
start = (start + 1) % capacity;
}
}
public double get(int index) {
if(index >= capacity) {
if (index >= capacity) {
throw new IndexOutOfBoundsException();
}
if(index >= size()) {
if (index >= size()) {
throw new IndexOutOfBoundsException();
}
return elements[(start + index) % capacity];
}
public boolean isEmpty() {
return start == -1 && end == -1;
}
public void clear() {
start = end = -1;
}
public CircularBuffer(int capacity) {
this.capacity = capacity;
elements = new double[capacity];
}
public double min() {
if(size() == 0) {
if (size() == 0) {
throw new NoSuchElementException();
}
double out = get(0);
for(int i = 1; i < size(); ++i) {
for (int i = 1; i < size(); ++i) {
out = Math.min(out, get(i));
}
return out;
}
public double max() {
if(size() == 0) {
if (size() == 0) {
throw new NoSuchElementException();
}
double out = get(0);
for(int i = 1; i < size(); ++i) {
for (int i = 1; i < size(); ++i) {
out = Math.max(out, get(i));
}
return out;
}
public int size() {
if(end == -1) {
if (end == -1) {
return 0;
}
return (end - start + capacity) % capacity + 1;
}
public int capacity() {
return capacity;
}

View File

@ -1,45 +1,50 @@
package processing.app.helpers;
public class Ticks {
private final int tickCount;
private final double[] ticks;
private double tickMin;
private double tickMax;
private double tickStep;
private int tickCount;
private double[] ticks;
public Ticks(double min, double max, int tickCount) {
public Ticks(double min, double max, int tickCount) {
double range = max - min;
double exp = Math.floor(Math.log10(range / (tickCount - 1)));
double exp;
if (range == 0.0) {
exp = 0;
} else {
exp = Math.floor(Math.log10(range / (tickCount - 1)));
}
double scale = Math.pow(10, exp);
double rawTickStep = (range / (tickCount - 1)) / scale;
for(double potentialStep : new double[] {1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}) {
if(potentialStep < rawTickStep) {
for (double potentialStep : new double[]{1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}) {
if (potentialStep < rawTickStep) {
continue;
}
tickStep = potentialStep * scale;
tickMin = tickStep * Math.floor(min / tickStep);
tickMax = tickMin + tickStep * (tickCount - 1);
if(tickMax >= max) {
if (tickMax >= max) {
break;
}
}
tickCount -= (int)Math.floor((tickMax - max) / tickStep);
tickCount -= (int) Math.floor((tickMax - max) / tickStep);
this.tickCount = tickCount;
ticks = new double[tickCount];
for(int i = 0; i < tickCount; ++i) {
for (int i = 0; i < tickCount; ++i) {
ticks[i] = tickMin + i * tickStep;
}
}
public double getTick(int i) {
return ticks[i];
}
public int getTickCount() {
return tickCount;
}