]> gitweb.hamatoma.de Git - jpinet/commitdiff
MultiChannelSheet works
authorhama <hama@siduction.net>
Tue, 12 Jul 2016 21:54:52 +0000 (23:54 +0200)
committerhama <hama@siduction.net>
Tue, 12 Jul 2016 21:54:52 +0000 (23:54 +0200)
src/main/java/de/republib/gui/Channel.java [new file with mode: 0644]
src/main/java/de/republib/gui/CurveSheet.java [new file with mode: 0644]
src/main/java/de/republib/gui/Diagram.java [deleted file]
src/main/java/de/republib/gui/MultiChannelSheet.java [new file with mode: 0644]
src/main/java/de/republib/pinet/gui/ControlCenter.java
src/main/java/de/republib/util/BytePairData.java
src/main/java/de/republib/util/FunctionPairData.java
src/main/java/de/republib/util/IPairData.java
src/main/java/de/republib/util/StringUtils.java [new file with mode: 0644]
src/test/java/de/republib/util/StringUtilsTest.java [new file with mode: 0644]

diff --git a/src/main/java/de/republib/gui/Channel.java b/src/main/java/de/republib/gui/Channel.java
new file mode 100644 (file)
index 0000000..0e1f9c8
--- /dev/null
@@ -0,0 +1,286 @@
+/**
+ *
+ */
+package de.republib.gui;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.util.LinkedList;
+import java.util.List;
+
+import de.republib.util.DoublePair;
+import de.republib.util.IPairData;
+import de.republib.util.PairData;
+
+/**
+ * Manages a set of curves as part of a <i>MultiChannelSheet</i>.
+ *
+ * A channel is an area in a diagram with its own real world coordinates. The
+ * channels are ordered vertically in the sheet.
+ *
+ * @author hm
+ *
+ */
+public class Channel {
+       private static final Color colorLegend = Color.BLACK;
+       private final int index;
+       private final MultiChannelSheet parent;
+       private double realX0 = 0.0;
+       private double realWidth = 1.0;
+       private double realY0 = 0.0;
+       private double realHeight = 1.0;
+       private int x0 = 0;
+       private int width = 800;
+       private int y0 = 0;
+       private int height = 600;
+       private final List<IPairData> curves = new LinkedList<IPairData>();
+
+       private final Color[] colorData = { Color.BLUE, Color.RED, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.DARK_GRAY,
+                       Color.ORANGE, Color.PINK };
+
+       /**
+        * Constructor.
+        *
+        * @param index
+        *            the index in the list of the parent
+        * @param parent
+        *            the multi channel sheet
+        */
+       public Channel(int index, MultiChannelSheet parent) {
+               this.index = index;
+               this.parent = parent;
+       }
+
+       /**
+        * @return the data
+        */
+       public List<IPairData> getCurves() {
+               return this.curves;
+       }
+
+       /**
+        * @return the height
+        */
+       public int getHeight() {
+               return this.height;
+       }
+
+       /**
+        * @return the index
+        */
+       public int getIndex() {
+               return this.index;
+       }
+
+       /**
+        * @return the parent
+        */
+       public MultiChannelSheet getParent() {
+               return this.parent;
+       }
+
+       /**
+        * @return the realHeight
+        */
+       public double getRealHeight() {
+               return this.realHeight;
+       }
+
+       /**
+        * @return the realWidth
+        */
+       public double getRealWidth() {
+               return this.realWidth;
+       }
+
+       /**
+        * @return the realX0
+        */
+       public double getRealX0() {
+               return this.realX0;
+       }
+
+       /**
+        * @return the realY0
+        */
+       public double getRealY0() {
+               return this.realY0;
+       }
+
+       /**
+        * @return the width
+        */
+       public int getWidth() {
+               return this.width;
+       }
+
+       /**
+        * @return the x0
+        */
+       public int getX0() {
+               return this.x0;
+       }
+
+       /**
+        * @return the y0
+        */
+       public int getY0() {
+               return this.y0;
+       }
+
+       /**
+        * Draw the channel.
+        *
+        * @param graphics
+        *            the graphical properties
+        */
+       public void paint(Graphics graphics) {
+               int no = -1;
+               for (final IPairData curve : this.curves) {
+                       graphics.setColor(this.colorData[++no % this.colorData.length]);
+                       final DoublePair minMax = PairData.minMaxY(curve, null);
+                       this.realX0 = curve.getX(0);
+                       this.realWidth = curve.getX(curve.getSteps() - 1) - this.realX0;
+                       this.realY0 = minMax.getValue1();
+                       final int steps = curve.getSteps();
+                       final double maxY = minMax.getValue2();
+                       this.realHeight = maxY + (maxY - this.realY0) / steps;
+                       int lastX = 0, lastY = 0;
+                       for (int ix = 0; ix < curve.getSteps(); ix++) {
+                               final int x = transformX(curve.getX(ix));
+                               final int y = transformY(curve.getY(ix));
+                               if (ix > 0) {
+                                       graphics.drawLine(lastX, lastY, x, y);
+                               }
+                               lastX = x;
+                               lastY = y;
+                       }
+               }
+               paintLegend(graphics);
+       }
+
+       /**
+        * Paints the scale data, title...
+        *
+        * @param graphics
+        *            the graphical properties
+        */
+       private void paintLegend(Graphics graphics) {
+               graphics.setFont(this.parent.getFont());
+               graphics.setColor(Channel.colorLegend);
+               final int x = this.x0 + this.width / 2;
+               final int y = this.y0 + this.parent.getFontSizeLegend() + 5;
+               final IPairData data = this.curves.get(0);
+               graphics.drawString(data.getName(), x, y);
+               String text = String.format("(%.1g, %.1g)", data.getX(0), data.getY(0));
+               graphics.drawString(text, this.x0 + 5, this.y0 + this.getHeight() - this.parent.getFontSizeLegend());
+               final int lastIx = data.getSteps() - 1;
+               text = String.format("(%.1g, %.1g)", data.getX(lastIx), data.getY(lastIx));
+               final int width = graphics.getFontMetrics().stringWidth(text);
+               graphics.drawString(text, this.x0 + this.width - width - 5, y);
+       }
+
+       /**
+        * @param height
+        *            the height to set
+        */
+       public void setHeight(int height) {
+               this.height = height;
+       }
+
+       /**
+        * @param realHeight
+        *            the realHeight to set
+        */
+       public void setRealHeight(double realHeight) {
+               this.realHeight = realHeight;
+       }
+
+       /**
+        * @param realWidth
+        *            the realWidth to set
+        */
+       public void setRealWidth(double realWidth) {
+               this.realWidth = realWidth;
+       }
+
+       /**
+        * @param realX0
+        *            the realX0 to set
+        */
+       public void setRealX0(double realX0) {
+               this.realX0 = realX0;
+       }
+
+       /**
+        * @param realY0
+        *            the realY0 to set
+        */
+       public void setRealY0(double realY0) {
+               this.realY0 = realY0;
+       }
+
+       /**
+        * @param width
+        *            the width to set
+        */
+       public void setWidth(int width) {
+               this.width = width;
+       }
+
+       /**
+        * @param x0
+        *            the x0 to set
+        */
+       public void setX0(int x0) {
+               this.x0 = x0;
+       }
+
+       /**
+        * @param y0
+        *            the y0 to set
+        */
+       public void setY0(int y0) {
+               this.y0 = y0;
+       }
+
+       /**
+        * Calculates the x position in pixel from a real x coordinate.
+        *
+        * @param x
+        *            the x coordinate in the real world
+        * @return the x value in pixel coordinate
+        */
+       public int transformX(double x) {
+               final double xPixel = (x - this.realX0) * (getWidth()) / (this.realWidth);
+               int rc;
+               if (xPixel < 0) {
+                       rc = 0;
+               } else if (xPixel > getWidth()) {
+                       rc = getWidth();
+               } else {
+                       rc = (int) Math.round(xPixel);
+               }
+               return this.x0 + rc;
+       }
+
+       /**
+        * Calculates the y position in pixel from a real y coordinate.
+        *
+        * @param y
+        *            the y coordinate in the real world
+        * @return the y value in pixel coordinate
+        */
+       public int transformY(double y) {
+               final double yPixel = (y - this.realY0) * (getHeight()) / this.realHeight;
+               int rc;
+               if (yPixel < 0) {
+                       rc = getHeight();
+               } else if (yPixel > getHeight()) {
+                       rc = -1;
+               } else {
+                       rc = getHeight() - (int) Math.round(yPixel);
+               }
+               return this.y0 + rc;
+       }
+}
diff --git a/src/main/java/de/republib/gui/CurveSheet.java b/src/main/java/de/republib/gui/CurveSheet.java
new file mode 100644 (file)
index 0000000..8b07c95
--- /dev/null
@@ -0,0 +1,226 @@
+/**
+ *
+ */
+package de.republib.gui;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.JPanel;
+
+import de.republib.util.DoublePair;
+import de.republib.util.IPairData;
+import de.republib.util.PairData;
+
+/**
+ * A diagram sheet.
+ *
+ * <pre>
+ * ----------------------------
+ * |<-- vertic. scale
+ * |
+ * |
+ * |
+ * |          v-- horiz. scale
+ * |__________________________
+ * -----------------------------
+ * </pre>
+ *
+ * @author hm
+ *
+ */
+public class CurveSheet extends JPanel {
+       /**
+        *
+        */
+       private static final long serialVersionUID = 1L;
+       private double realX0 = 0.0;
+       private double realY0 = 0.0;
+       private double realWidth = 1.0;
+       private double realHeight = 1.0;
+       /// distance horizontal scale to left border
+       private final int hScaleDistance = 5;
+       /// distance vertical scale to bottom
+       private final int vScaleDistance = 5;
+       private final int widthLine = 3;
+       private final Color colorScale = Color.DARK_GRAY;
+       // private final Color colorSheet = Color.WHITE;
+       private final int fontSizeLegend = 16;
+       private final List<IPairData> data = new LinkedList<IPairData>();
+       private final Color[] colorData = { Color.BLUE, Color.RED, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.DARK_GRAY,
+                       Color.ORANGE, Color.PINK };
+       private Font font = null;
+
+       /**
+        * Appends a data package for drawing one line.
+        *
+        * @param data
+        *            the pair data for one line
+        * @return the instance (for chaining)
+        */
+       public CurveSheet addData(IPairData data) {
+               this.data.add(data);
+               return this;
+       }
+
+       /*
+        * (non-Javadoc)
+        *
+        * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
+        */
+       @Override
+       protected void paintComponent(Graphics graphics) {
+               super.paintComponent(graphics);
+               paintScale(graphics);
+               paintData(graphics);
+               paintLegend(graphics);
+       }
+
+       /**
+        * Paints the data as lines in the sheet.
+        *
+        * @param graphics
+        *            the graphic environment
+        */
+       protected void paintData(Graphics graphics) {
+               final double x0 = this.realX0;
+               final double realWidth = this.realWidth;
+               final double y0 = this.realY0;
+               final double realHeight = this.realHeight;
+               if (this.font == null) {
+                       this.font = new Font(graphics.getFont().getName(), graphics.getFont().getStyle(), this.fontSizeLegend);
+
+               }
+               final Graphics2D graphics2 = (Graphics2D) graphics;
+               graphics2.setStroke(new BasicStroke(this.widthLine));
+               int no = -1;
+               for (final IPairData data : this.data) {
+                       graphics.setColor(this.colorData[++no % this.colorData.length]);
+                       final DoublePair minMax = PairData.minMaxY(data, null);
+                       this.realX0 = data.getX(0);
+                       this.realWidth = data.getX(data.getSteps() - 1) - this.realX0;
+                       this.realY0 = minMax.getValue1();
+                       final int steps = data.getSteps();
+                       final double maxY = minMax.getValue2();
+                       this.realHeight = maxY + (maxY - this.realY0) / steps;
+                       int lastX = 0, lastY = 0;
+                       for (int ix = 0; ix < data.getSteps(); ix++) {
+                               final int x = transformX(data.getX(ix));
+                               final int y = transformY(data.getY(ix));
+                               if (ix > 0) {
+                                       graphics.drawLine(lastX, lastY, x, y);
+                               }
+                               lastX = x;
+                               lastY = y;
+                       }
+               }
+               this.realX0 = x0;
+               this.realWidth = realWidth;
+               this.realY0 = y0;
+               this.realHeight = realHeight;
+       }
+
+       /**
+        * Paints the data as lines in the sheet.
+        *
+        * @param graphics
+        *            the graphic environment
+        */
+       protected void paintLegend(Graphics graphics) {
+               int no = 1;
+               final int x = getWidth() / 2;
+               graphics.setFont(this.font);
+               for (final IPairData data : this.data) {
+                       final int y = (no + no / 4) * this.fontSizeLegend;
+                       graphics.setColor(this.colorData[(no - 1) % this.colorData.length]);
+                       graphics.drawString(data.getName(), x, y);
+                       graphics.drawLine(x - 20, y, x - 5, y);
+                       ++no;
+               }
+       }
+
+       /**
+        * Paints the scales.
+        *
+        * @param graphics
+        *            the graphic environment
+        */
+       protected void paintScale(Graphics graphics) {
+               graphics.setColor(this.colorScale);
+               final int y0 = this.getHeight() - this.vScaleDistance;
+               final int y1 = this.vScaleDistance;
+               final int x0 = this.hScaleDistance;
+               final int x1 = this.getWidth() - this.hScaleDistance;
+               // vertical scale:
+               // graphics.drawLine(x0, y0, x0, y1);
+               // horizontal scale:
+               // graphics.drawLine(x0, y0, x1, y0);
+               int no = 1;
+               final int x = getWidth() / 2;
+               graphics.setFont(this.font);
+               final int textWidth = 75;
+               for (final IPairData data : this.data) {
+                       final int yBottom = (no + no / 4) * this.fontSizeLegend;
+                       graphics.setColor(this.colorData[(no - 1) % this.colorData.length]);
+                       // bottom left to-top: xMin
+                       String text = String.format("x0: %.1g", data.getX(0));
+                       graphics.drawString(text, 2, y0 - this.fontSizeLegend / 2);
+                       // bottom left to-right: yMin
+                       text = String.format("y0: %.1g", data.getY(0));
+                       graphics.drawString(text, textWidth * no, y0 - this.fontSizeLegend / 2);
+                       // top left: to-right: yMax
+                       text = String.format("y1: %.1g", data.getY(data.getSteps() - 1));
+                       graphics.drawString(text, textWidth * no + textWidth * no / 8, 3 * this.fontSizeLegend / 2);
+                       // top right: to-bottom: xMax
+                       text = String.format("x1: %.1g", data.getX(data.getSteps() - 1));
+                       graphics.drawString(text, this.getWidth() - textWidth,
+                                       no * this.fontSizeLegend + no * this.fontSizeLegend / 8);
+                       ++no;
+               }
+       }
+
+       /**
+        * Calculates the x position in pixel from a real x coordinate.
+        *
+        * @param x
+        *            the x coordinate in the real world
+        * @return the x value in pixel coordinate
+        */
+       public int transformX(double x) {
+               final double xPixel = (x - this.realX0) * (getWidth() - 2 * this.hScaleDistance) / (this.realWidth);
+               int rc;
+               if (xPixel < 0) {
+                       rc = 0;
+               } else if (xPixel > getWidth()) {
+                       rc = getWidth();
+               } else {
+                       rc = (int) Math.round(xPixel);
+               }
+               return rc;
+       }
+
+       /**
+        * Calculates the y position in pixel from a real y coordinate.
+        *
+        * @param y
+        *            the y coordinate in the real world
+        * @return the y value in pixel coordinate
+        */
+       public int transformY(double y) {
+               final double yPixel = (y - this.realY0) * (getHeight() - 2 * this.vScaleDistance) / this.realHeight;
+               int rc;
+               if (yPixel < 0) {
+                       rc = getHeight();
+               } else if (yPixel > getHeight()) {
+                       rc = -1;
+               } else {
+                       rc = getHeight() - (int) Math.round(yPixel);
+               }
+               return rc;
+       }
+}
diff --git a/src/main/java/de/republib/gui/Diagram.java b/src/main/java/de/republib/gui/Diagram.java
deleted file mode 100644 (file)
index f538ea5..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- *
- */
-package de.republib.gui;
-
-import java.awt.Color;
-import java.awt.Graphics;
-import java.util.LinkedList;
-import java.util.List;
-
-import javax.swing.JPanel;
-
-import de.republib.util.DoublePair;
-import de.republib.util.IPairData;
-import de.republib.util.PairData;
-
-/**
- * A diagram sheet.
- *
- * <pre>
- * ----------------------------
- * |<-- vertic. scale
- * |
- * |
- * |
- * |          v-- horiz. scale
- * |__________________________
- * -----------------------------
- * </pre>
- *
- * @author hm
- *
- */
-public class Diagram extends JPanel {
-       private double realX0 = 0.0;
-       private double realY0 = 0.0;
-       private double realWidth = 1.0;
-       private double realHeight = 1.0;
-       /// 0.0: no fix ration. Otherwise: width / height
-       private final double ratio = 800.0 / 600.0;
-       /// distance horizontal scale to left border
-       private final int hScaleDistance = 5;
-       /// distance vertical scale to bottom
-       private final int vScaleDistance = 5;
-       private final int scaleWidth = 1;
-       private final Color colorScale = Color.DARK_GRAY;
-       private final Color colorScaleFont = Color.BLACK;
-       private final Color colorSheet = Color.WHITE;
-       private final List<IPairData> data = new LinkedList<IPairData>();
-
-       /**
-        * Appends a data package for drawing one line.
-        *
-        * @param data
-        *            the pair data for one line
-        * @return the instance (for chaining)
-        */
-       public Diagram addData(IPairData data) {
-               this.data.add(data);
-               return this;
-       }
-
-       @Override
-       protected void paintComponent(Graphics graphics) {
-               super.paintComponent(graphics);
-               paintScale(graphics);
-               paintData(graphics);
-       }
-
-       /**
-        * Paints the data as lines in the sheet.
-        *
-        * @param graphics
-        *            the graphic environment
-        */
-       protected void paintData(Graphics graphics) {
-               final double x0 = this.realX0;
-               final double realWidth = this.realWidth;
-               final double y0 = this.realY0;
-               final double realHeight = this.realHeight;
-               for (final IPairData data : this.data) {
-                       final DoublePair minMax = PairData.minMaxY(data, null);
-                       this.realX0 = data.getX(0);
-                       this.realWidth = data.getX(data.getSteps() - 1) - this.realX0;
-                       this.realY0 = minMax.getValue1();
-                       final int steps = data.getSteps();
-                       final double maxY = minMax.getValue2();
-                       this.realHeight = maxY + (maxY - this.realY0) / steps;
-                       int lastX = 0, lastY = 0;
-                       for (int ix = 0; ix < data.getSteps(); ix++) {
-                               final int x = transformX(data.getX(ix));
-                               final int y = transformY(data.getY(ix));
-                               if (ix > 0) {
-                                       graphics.drawLine(lastX, lastY, x, y);
-                               }
-                               lastX = x;
-                               lastY = y;
-                       }
-               }
-               this.realX0 = x0;
-               this.realWidth = realWidth;
-               this.realY0 = y0;
-               this.realHeight = realHeight;
-       }
-
-       /**
-        * Paints the scales.
-        *
-        * @param graphics
-        *            the graphic environment
-        */
-       protected void paintScale(Graphics graphics) {
-               graphics.setColor(this.colorScale);
-               final int y0 = this.getHeight() - this.vScaleDistance;
-               final int y1 = this.vScaleDistance;
-               final int x0 = this.hScaleDistance;
-               final int x1 = this.getWidth() - this.hScaleDistance;
-               // vertical scale:
-               graphics.drawLine(x0, y0, x0, y1);
-               // horizontal scale:
-               graphics.drawLine(x0, y0, x1, y0);
-
-       }
-
-       /**
-        * Calculates the x position in pixel from a real x coordinate.
-        *
-        * @param x
-        *            the x coordinate in the real world
-        * @return the x value in pixel coordinate
-        */
-       public int transformX(double x) {
-               final double xPixel = (x - this.realX0) * (getWidth() - 2 * this.hScaleDistance);
-               int rc;
-               if (xPixel < 0) {
-                       rc = 0;
-               } else if (xPixel > getWidth()) {
-                       rc = getWidth();
-               } else {
-                       rc = (int) Math.round(xPixel);
-               }
-               return rc;
-       }
-
-       /**
-        * Calculates the y position in pixel from a real y coordinate.
-        *
-        * @param y
-        *            the y coordinate in the real world
-        * @return the y value in pixel coordinate
-        */
-       public int transformY(double y) {
-               final double yPixel = (y - this.realY0) * (getHeight() - 2 * this.vScaleDistance);
-               int rc;
-               if (yPixel < 0) {
-                       rc = getHeight();
-               } else if (yPixel > getHeight()) {
-                       rc = -1;
-               } else {
-                       rc = getHeight() - (int) Math.round(yPixel);
-               }
-               return rc;
-       }
-}
diff --git a/src/main/java/de/republib/gui/MultiChannelSheet.java b/src/main/java/de/republib/gui/MultiChannelSheet.java
new file mode 100644 (file)
index 0000000..1619e4f
--- /dev/null
@@ -0,0 +1,229 @@
+/**
+ *
+ */
+package de.republib.gui;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.util.ArrayList;
+
+import javax.swing.JPanel;
+
+import de.republib.util.IPairData;
+
+/**
+ * Manages a diagram with multiple <strong>channels</strong>.
+ *
+ * A channel is an area in a diagram with its own real world coordinates. The
+ * channels are ordered in a grid.<br>
+ * <br>
+ * Screen coordinates:<br>
+ * All channels of a column have the same width. Column widths can be different.
+ * Channel height can be different.
+ *
+ * <pre>
+ * __________________________
+ * | channel0 : channel3
+ * --------------------------
+ * | channel1 : channel4
+ * --------------------------
+ * | channel2 :
+ * __________________________
+ * </pre>
+ *
+ * @author hm
+ *
+ */
+public class MultiChannelSheet extends JPanel {
+       /**
+        * Serialization UnifiedId.
+        */
+       private static final long serialVersionUID = 1L;
+
+       private final int columnCount = 2;
+       private final int rowCount = 4;
+       private final ArrayList<Channel> channels = new ArrayList<>();
+       private final int widthGridLine = 1;
+       private final Color colorGridLine = Color.BLACK;
+       private Font font = null;
+       private final int fontSizeLegend = 14;
+
+       /**
+        * Constructor.
+        */
+       public MultiChannelSheet() {
+               addComponentListener(new ResizeListener(this));
+       }
+
+       /**
+        * Adds a channel to the sheet.
+        *
+        * @param channel
+        *            the channel to add
+        * @return the instance (for chaining)
+        */
+       public MultiChannelSheet addChannel(IPairData data) {
+               final Channel channel = new Channel(this.channels.size(), this);
+               this.channels.add(channel);
+               channel.getCurves().add(data);
+               return this;
+       }
+
+       /**
+        * Returns the n-th channel.
+        *
+        * @param index
+        *            index of the wanted channel.
+        * @return <i>null</i>: wrong index<br>
+        *         otherwise: the index-th channel
+        */
+       Channel getChannel(int index) {
+               return index >= 0 && index < this.channels.size() ? this.channels.get(index) : null;
+       }
+
+       /**
+        * @return the channelCount
+        */
+       public int getChannelCount() {
+               return this.channels.size();
+       }
+
+       /**
+        * @return the font
+        */
+       @Override
+       public Font getFont() {
+               return this.font;
+       }
+
+       /**
+        * @return the fontSizeLegend
+        */
+       public int getFontSizeLegend() {
+               return this.fontSizeLegend;
+       }
+
+       /**
+        * Handles a resize event.
+        */
+       public void onResize() {
+               resizeChannels();
+       }
+
+       /*
+        * (non-Javadoc)
+        *
+        * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
+        */
+       @Override
+       protected void paintComponent(Graphics graphics) {
+               if (this.font == null) {
+                       this.font = new Font(graphics.getFont().getName(), graphics.getFont().getStyle(), this.fontSizeLegend);
+               }
+               super.paintComponent(graphics);
+               paintGrid(graphics);
+               final int count = getChannelCount();
+               for (int index = 0; index < count; index++) {
+                       final Channel channel = this.channels.get(index);
+                       channel.paint(graphics);
+               }
+       }
+
+       /**
+        * Paints the grid lines separating the channel areas.
+        *
+        * @param graphics
+        *            the graphics properties
+        */
+       protected void paintGrid(Graphics graphics) {
+               graphics.setColor(this.colorGridLine);
+               final int x1 = this.getWidth();
+               final int y1 = this.getHeight();
+               final int height = y1 / this.rowCount;
+               final int width = x1 / this.columnCount;
+               // vertical lines:
+               for (int ix = 1; ix < this.columnCount; ix++) {
+                       final int x = ix * width;
+                       graphics.drawLine(x, 0, x, y1);
+               }
+               // horizontal lines:
+               for (int ix = 1; ix < this.rowCount; ix++) {
+                       final int y = ix * height;
+                       graphics.drawLine(0, y, x1, y);
+               }
+       }
+
+       /**
+        * Divides the current sheet to the current channels.
+        */
+       private void resizeChannels() {
+               final int count = getChannelCount();
+               int lastY = 0;
+               int lastX = 0;
+               final int height = getHeight() / this.rowCount;
+               int width = getWidth() / this.columnCount;
+               for (int index = 0; index < count; index++) {
+                       final Channel channel = this.channels.get(index);
+                       final int col = index / this.rowCount;
+                       final int row = index % this.rowCount;
+                       channel.setY0(lastY);
+                       channel.setX0(lastX);
+                       channel.setWidth(width - this.widthGridLine);
+                       if (row % this.rowCount == this.rowCount - 1) {
+                               channel.setHeight(getHeight() - lastY);
+                               lastY = 0;
+                               lastX += width;
+                               if (col == this.columnCount - 1) {
+                                       width = getWidth() - lastX;
+                               }
+                       } else {
+                               channel.setHeight(height - this.widthGridLine);
+                               lastY += height;
+                       }
+               }
+       }
+
+       /**
+        * @param channelCount
+        *            the channelCount to set
+        */
+       public void setChannelCount(int channelCount) {
+               boolean changed = false;
+               while (this.channels.size() > channelCount) {
+                       this.channels.remove(this.channels.size() - 1);
+                       changed = true;
+               }
+               while (this.channels.size() < channelCount) {
+                       this.channels.add(new Channel(this.channels.size(), this));
+                       changed = true;
+               }
+               if (changed) {
+                       resizeChannels();
+               }
+       }
+
+       /**
+        * @param font
+        *            the font to set
+        */
+       @Override
+       public void setFont(Font font) {
+               this.font = font;
+       }
+}
+
+class ResizeListener extends ComponentAdapter {
+       MultiChannelSheet sheet;
+
+       ResizeListener(MultiChannelSheet sheet) {
+               this.sheet = sheet;
+       }
+
+       @Override
+       public void componentResized(ComponentEvent e) {
+               this.sheet.onResize();
+       }
+}
index a08d12362ceff29d1da8ecaeeb5339eaefc2b42a..61f7692a81c830f62516ef253d79ed0cc99055f4 100644 (file)
@@ -11,7 +11,7 @@ import javax.swing.JPanel;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
 
-import de.republib.gui.Diagram;
+import de.republib.gui.MultiChannelSheet;
 import de.republib.util.FunctionPairData;
 import de.republib.util.I18N;
 import de.republib.util.MathFunction;
@@ -25,7 +25,7 @@ public class ControlCenter {
        private JPanel panelOutput;
        private JPanel panelLog;
        private JLabel labelStatusLine;
-       private Diagram panelData;
+       private MultiChannelSheet panelData;
        private JPanel panelHead;
        private JTextField textFieldServer;
        private JTextField textFieldPort;
@@ -49,15 +49,27 @@ public class ControlCenter {
 
                this.panelCenter.add(this.tabbedPane = new JTabbedPane());
                this.tabbedPane.addTab(I18N.tr("Output"), this.panelOutput = new JPanel());
-               this.tabbedPane.addTab(I18N.tr("Diagram"), this.panelData = new Diagram());
+               this.tabbedPane.addTab(I18N.tr("Diagram"), this.panelData = new MultiChannelSheet());
                FunctionPairData function = new FunctionPairData(MathFunction.X, 0.0, 10.0, 790);
                function.setFactor(9.0);
                function.setOffset(1.0);
-               this.panelData.addData(function);
+               this.panelData.addChannel(function);
                function = new FunctionPairData(MathFunction.SIN, 0.0, 10.0, 790);
                function.setFactor(4.0);
                function.setOffset(5.0);
-               this.panelData.addData(function);
+               this.panelData.addChannel(function);
+               function = new FunctionPairData(MathFunction.COS, 0.0, 10.0, 790);
+               function.setFactor(4.0);
+               function.setOffset(5.0);
+               this.panelData.addChannel(function);
+               function = new FunctionPairData(MathFunction.LOG, 0.01, 10.0, 790);
+               this.panelData.addChannel(function);
+               function = new FunctionPairData(MathFunction.EXP, -5.0, 5.0, 790);
+               this.panelData.addChannel(function);
+               function = new FunctionPairData(MathFunction.X2, -1, 2, 790);
+               this.panelData.addChannel(function);
+               function = new FunctionPairData(MathFunction.TAN, 0.0, 1.0, 790);
+               this.panelData.addChannel(function);
                this.tabbedPane.addTab(I18N.tr("Log"), this.panelLog = new JPanel());
        }
 
index 06c10e6aa43bff368028d16dc03fbe2be226b7d1..9bedf061053a9bd0aabf86471c8f7921c4b0234c 100644 (file)
@@ -18,6 +18,8 @@ public class BytePairData extends DynBytes implements IPairData {
 
        private final int dataWidth;
 
+       private String name;
+
        /**
         * Constructor.
         *
@@ -30,9 +32,10 @@ public class BytePairData extends DynBytes implements IPairData {
        public BytePairData(int capacity, int blocksize, double minX, double maxX, int dataWidth) {
                super(capacity, blocksize);
                if (dataWidth < 0 || dataWidth > 8) {
-                       BytePairData.logger.error("getY(): illegal data width: {:d}", dataWidth);
+                       BytePairData.logger.error("getY(): illegal data width: %d", dataWidth);
                        dataWidth = 1;
                }
+               this.name = String.format("%d bit data", 8 * dataWidth);
                this.minX = minX;
                this.maxX = maxX;
                this.dataWidth = dataWidth;
@@ -59,6 +62,14 @@ public class BytePairData extends DynBytes implements IPairData {
                return this.minX;
        }
 
+       /**
+        * @return the name
+        */
+       @Override
+       public String getName() {
+               return this.name;
+       }
+
        /*
         * (non-Javadoc)
         *
@@ -119,4 +130,12 @@ public class BytePairData extends DynBytes implements IPairData {
                return y;
        }
 
+       /**
+        * @param name
+        *            the name to set
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
 }
index a4cf5d1b53342664e21f220784aa229f3cef0b62..a3153739edca4f288fc50ffd3ded783192caeadd 100644 (file)
@@ -24,8 +24,11 @@ public class FunctionPairData implements IPairData {
 
        private final int steps;
 
+       private String name;
+
        public FunctionPairData(MathFunction function, double xMin, double xMax, int steps) {
                this.function = function;
+               this.name = this.function.name();
                this.xMax = xMax;
                this.xMin = xMin;
                this.steps = steps;
@@ -52,6 +55,14 @@ public class FunctionPairData implements IPairData {
                return this.function;
        }
 
+       /**
+        * @return the name
+        */
+       @Override
+       public String getName() {
+               return this.name;
+       }
+
        /**
         * @return the offset
         */
@@ -129,7 +140,8 @@ public class FunctionPairData implements IPairData {
                default:
                        break;
                }
-               return y * this.factor + this.offset;
+               y = y * this.factor + this.offset;
+               return y;
        }
 
        /**
@@ -148,6 +160,14 @@ public class FunctionPairData implements IPairData {
                this.factor2 = factor2;
        }
 
+       /**
+        * @param name
+        *            the name to set
+        */
+       public void setName(String name) {
+               this.name = name;
+       }
+
        /**
         * @param offset
         *            the offset to set
index 257abcaeb85f54e117d6cc987f9e81dd4c153a43..e1971de1c8f260018674a48b0a979dda139c6c21 100644 (file)
@@ -11,6 +11,13 @@ package de.republib.util;
  */
 public interface IPairData {
 
+       /**
+        * Gets a name for the legend.
+        *
+        * @return a name of the data
+        */
+       String getName();
+
        /**
         * Returns the number of (x, y) pairs.
         *
@@ -35,4 +42,12 @@ public interface IPairData {
         * @return the <i>index</i>-th x value
         */
        double getY(int index);
+
+       /**
+        * Sets the name for the legend.
+        *
+        * @param name
+        *            the name to set
+        */
+       void setName(String name);
 }
diff --git a/src/main/java/de/republib/util/StringUtils.java b/src/main/java/de/republib/util/StringUtils.java
new file mode 100644 (file)
index 0000000..26b2aa1
--- /dev/null
@@ -0,0 +1,86 @@
+package de.republib.util;
+
+/**
+ * Helper class for strings.
+ *
+ * @author hm
+ *
+ */
+public class StringUtils {
+       /**
+        * Formats a double value.
+        *
+        * Wanted: a short string as possible.
+        *
+        * @param value
+        *            value to format
+        * @param precision
+        *            number of "valid digits": 0.034 and 1.2E99 have 2 valid
+        *            digits<br>
+        *            <i>precision</i> in [1, 3]
+        * @return the formatted strings
+        */
+       public static String formatFloat(double value, int precision) {
+               String rc = null;
+               if (precision <= 0) {
+                       precision = 1;
+               }
+               if (value >= 1E6) {
+                       rc = String.format("%.g", precision - 1, value);
+               } else if (value > 1.0) {
+                       double limit;
+                       int prec = 0;
+                       switch (precision) {
+                       case 1:
+                               limit = 10.0;
+                               prec = 0;
+                               break;
+                       case 2:
+                               limit = 100.0;
+                               prec = 1;
+                               break;
+                       case 3:
+                               limit = 1000.0;
+                               prec = 2;
+                               break;
+                       default:
+                               limit = 1000.0;
+                               break;
+                       }
+                       if (value >= limit) {
+                               rc = String.format("%d", (int) Math.round(value));
+                       } else {
+                               rc = String.format("%.*g", prec, value);
+                       }
+               } else if (value >= 1E-5) {
+                       final double limit;
+                       int prec;
+                       switch (precision) {
+                       case 1:
+                               limit = 1E-4;
+                               prec = 5;
+                               break;
+                       case 2:
+                               limit = 1E-3;
+                               prec = 4;
+                               break;
+                       case 3:
+                               limit = 1E-2;
+                               prec = 3;
+                               break;
+                       default:
+                               limit = 1E-2;
+                               prec = 3;
+                               break;
+                       }
+                       if (value < limit) {
+
+                       } else {
+                               rc = String.format("%.*f", prec, value);
+                       }
+               } else {
+                       rc = String.format("%.*g", precision - 1, value);
+               }
+               return rc;
+       }
+}
diff --git a/src/test/java/de/republib/util/StringUtilsTest.java b/src/test/java/de/republib/util/StringUtilsTest.java
new file mode 100644 (file)
index 0000000..799e4e5
--- /dev/null
@@ -0,0 +1,34 @@
+package de.republib.util;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class StringUtilsTest {
+
+       private void checkPrec1() {
+               final double value = 1234567.77;
+               Assert.assertEquals(StringUtils.formatFloat(value, 3), "123E6");
+               Assert.assertEquals(StringUtils.formatFloat(123456.77, 3), "123456");
+               Assert.assertEquals(StringUtils.formatFloat(12345.77, 3), "123456");
+               Assert.assertEquals(StringUtils.formatFloat(1234.77, 3), "12345");
+               Assert.assertEquals(StringUtils.formatFloat(123.77, 3), "123");
+               Assert.assertEquals(StringUtils.formatFloat(12.77, 3), "12.7");
+               Assert.assertEquals(StringUtils.formatFloat(1.77, 3), "1.77");
+
+       }
+
+       private void checkPrec2() {
+
+       }
+
+       private void checkPrec3() {
+
+       }
+
+       @Test
+       public void formatFloat() {
+               checkPrec1();
+               checkPrec2();
+               checkPrec3();
+       }
+}