/**
* Squidy Interaction Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Squidy Interaction Library is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.designer.shape.visualization.impl;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.text.DecimalFormat;
import org.squidy.designer.shape.VisualShape;
import org.squidy.designer.shape.visualization.PlotContext;
import org.squidy.designer.shape.visualization.Visualization;
import org.squidy.designer.util.StrokeUtils;
import org.squidy.manager.data.IData;
import org.squidy.manager.data.IDataContainer;
import org.squidy.manager.data.impl.DataAnalog;
import org.squidy.manager.data.impl.DataButton;
import org.squidy.manager.data.impl.DataDigital;
import org.squidy.manager.data.impl.DataInertial;
import org.squidy.manager.data.impl.DataKey;
import org.squidy.manager.data.impl.DataPosition2D;
import org.squidy.manager.data.impl.DataPosition3D;
import org.squidy.manager.data.impl.DataPosition6D;
import org.squidy.manager.data.impl.DataString;
import edu.umd.cs.piccolo.util.PAffineTransform;
import edu.umd.cs.piccolo.util.PPaintContext;
/**
* <code>ScatterPlot</code>.
*
* <pre>
* Date: May 30, 2009
* Time: 4:50:18 PM
* </pre>
*
*
* @author
* Roman R�dle
* <a href="mailto:Roman.Raedle@uni-konstanz.de">Roman.Raedle@uni-konstanz.de</a>
* Human-Computer Interaction Group
* University of Konstanz
*
* @version $Id: ScatterPlot.java 772 2011-09-16 15:39:44Z raedle $
* @since 1.0.0
*/
public class ScatterPlot implements Visualization {
// Affine transform scales sx to 0.5 and sy to 0.5.
private static final PAffineTransform SCALE_TRANSFORM = new PAffineTransform();
static {
SCALE_TRANSFORM.setToScale(0.5, 0.5);
}
// Affine transform rotates graphics -45 degrees.
private static final PAffineTransform ROTATION_TRANSFORM = new PAffineTransform();
private static final double ROTATION_RADIANS = Math.toRadians(-45);
static {
ROTATION_TRANSFORM.setToRotation(ROTATION_RADIANS);
}
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.##");
/**
* @param plotContext
* @param dataContainter
*/
public void plotData(PlotContext plotContext, IDataContainer dataContainer) {
Graphics2D g = plotContext.getGraphics();
int width = plotContext.getWidth();
int height = plotContext.getHeight();
long sampleTime = plotContext.getSampleTime();
long currentTime = System.currentTimeMillis();
double normalizedWidth = Math.abs(((currentTime - dataContainer.getTimestamp()) / (double) sampleTime) - 1);
double xPosition = width * normalizedWidth;
for (IData data : dataContainer.getData()) {
if (data.getClass().equals(DataPosition2D.class)) {
plot(g, (DataPosition2D) data, height, xPosition, Color.RED, Color.BLUE);
}
else if (data.getClass().equals(DataPosition3D.class)) {
plot(g, (DataPosition3D) data, height, xPosition, Color.MAGENTA, Color.CYAN, Color.GRAY);
}
else if (data.getClass().equals(DataPosition6D.class)) {
plot(g, (DataPosition3D) data, height, xPosition, Color.MAGENTA, Color.CYAN, Color.GRAY);
}
else if (data.getClass().equals(DataAnalog.class)) {
plot(g, (DataAnalog) data, height, xPosition);
}
else if (data.getClass().equals(DataDigital.class)) {
plot(plotContext, (DataDigital) data, height, xPosition);
}
else if (data.getClass().equals(DataButton.class)) {
plot(plotContext, (DataDigital) data, height, xPosition);
}
else if (data.getClass().equals(DataKey.class)) {
plot(plotContext, (DataDigital) data, height, xPosition);
}
else if (data.getClass().equals(DataString.class)) {
plot(plotContext, (DataString) data, height, xPosition);
}
else if (data.getClass().equals(DataInertial.class)) {
plot(g, (DataInertial) data, height, xPosition);
}
}
}
/**
* @param g
* @param dataPosition2D
* @param height
*/
private void plot(Graphics2D g, DataPosition2D dataPosition2D, int height, double xPosition, Color xColor, Color yColor) {
// Draw x-position of DataPosition2D.
g.setColor(xColor);
int dataPosition2DXPosY = (int) (Math.abs(dataPosition2D.getX() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition2DXPosY, 50, 50);
// Draw y-position of DataPosition2D.
g.setColor(yColor);
int dataPosition2DYPosY = (int) (Math.abs(dataPosition2D.getY() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition2DYPosY, 50, 50);
}
/**
* @param g
* @param dataPosition2D
* @param height
*/
private void plot(Graphics2D g, DataPosition3D dataPosition3D, int height, double xPosition, Color xColor, Color yColor, Color zColor) {
// Draw x-position of DataPosition2D.
g.setColor(xColor);
int dataPosition3DXPosY = (int) (Math.abs(dataPosition3D.getX() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition3DXPosY, 50, 50);
// Draw y-position of DataPosition2D.
g.setColor(yColor);
int dataPosition3DYPosY = (int) (Math.abs(dataPosition3D.getY() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition3DYPosY, 50, 50);
// Draw z-position of DataPosition2D.
g.setColor(zColor);
int dataPosition3DZPosY = (int) (Math.abs(dataPosition3D.getZ() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition3DZPosY, 50, 50);
}
/**
* @param g
* @param dataAnalog
* @param height
*/
private void plot(Graphics2D g, DataAnalog dataAnalog, int height, double xPosition) {
// Draw value of DataAnalog.
g.setColor(Color.GREEN);
int dataPosition2DXPosY = (int) (Math.abs(dataAnalog.getValue() - 1.0) * height);
g.fillOval((int) xPosition, dataPosition2DXPosY, 50, 50);
}
/**
* @param g
* @param dataInertial
* @param height
* @param xPosition
*/
private void plot(Graphics2D g, DataInertial dataInertial, int height, double xPosition) {
// Draw x-value of dataInertial.
g.setColor(Color.YELLOW);
int dataInertialXPosY = (int) (Math.abs(dataInertial.getX() - 1.0) * height);
g.fillOval((int) xPosition, dataInertialXPosY, 50, 50);
// Draw y-value of dataInertial.
g.setColor(Color.MAGENTA);
int dataInertialYPosY = (int) (Math.abs(dataInertial.getY() - 1.0) * height);
g.fillOval((int) xPosition, dataInertialYPosY, 50, 50);
// Draw z-value of dataInertial.
g.setColor(Color.CYAN);
int dataInertialZPosY = (int) (Math.abs(dataInertial.getZ() - 1.0) * height);
g.fillOval((int) xPosition, dataInertialZPosY, 50, 50);
// Draw vector length of dataInertial.
g.setColor(Color.GRAY);
int dataInertialAbs = (int) (Math.abs(dataInertial.getAbsoluteValue() - 1.0) * height);
g.fillOval((int) xPosition, dataInertialAbs, 50, 50);
}
private static final String VISUALIZATION_NAME = "Scatter Plot";
private static final Font FONT = VisualShape.internalFont.deriveFont(180f);
private static String LABEL_X_AXIS;
private static int LABEL_X_AXIS_X;
private static int LABEL_X_AXIS_Y;
private static String LABEL_Y_AXIS;
private static int LABEL_Y_AXIS_X;
private static int LABEL_Y_AXIS_Y;
private static final PAffineTransform ROTATION_90_COUNTER = new PAffineTransform();
static {
ROTATION_90_COUNTER.rotate(Math.toRadians(-90));
}
/* (non-Javadoc)
* @see org.squidy.designer.shape.visualization.Visualization#paintContextual(edu.umd.cs.piccolo.util.PPaintContext, org.squidy.designer.shape.visualization.PlotContext)
*/
public void paintContextual(PPaintContext paintContext, PlotContext plotContext) {
Graphics2D g = paintContext.getGraphics();
long sampleTime = plotContext.getSampleTime();
g.setFont(FONT);
g.setColor(Color.BLACK);
g.drawString(VISUALIZATION_NAME, 0, 0 - 100);
int width = plotContext.getWidth();
int height = plotContext.getHeight();
int timeStep = (int) sampleTime / 500;
int gridStepX = (int) (width / timeStep);
int gridStepY = (int) (height / 10);
// Paint axes.
g.setStroke(StrokeUtils.getBasicStroke(25f));
g.drawLine(0, 0, 0, (int) height);
g.drawLine(0, (int) height, (int) width, (int) height);
FontMetrics fm = g.getFontMetrics();
// Properties for vertical and horizontal lines.
int requiredWidth = (int) width;
int requiredHeight = (int) height;
if (LABEL_X_AXIS == null) {
LABEL_X_AXIS = "Time (ms)";
LABEL_X_AXIS_X = requiredWidth / 2 - g.getFontMetrics().stringWidth(LABEL_X_AXIS);
LABEL_X_AXIS_Y = requiredHeight + 5 * 100;
}
g.drawString(LABEL_X_AXIS, LABEL_X_AXIS_X, LABEL_X_AXIS_Y);
paintContext.pushTransform(ROTATION_90_COUNTER);
if (LABEL_Y_AXIS == null) {
LABEL_Y_AXIS = "Value";
LABEL_Y_AXIS_X = -(requiredHeight / 2) - (g.getFontMetrics().stringWidth(LABEL_Y_AXIS) / 2);
LABEL_Y_AXIS_Y = -5 * 100;
}
g.drawString(LABEL_Y_AXIS, LABEL_Y_AXIS_X, LABEL_Y_AXIS_Y);
paintContext.popTransform(ROTATION_90_COUNTER);
g.setStroke(StrokeUtils.getBasicStroke(5.5f));
// Paint vertical lines.
int timeAxes = (int) sampleTime;
for (int xShift = 0; xShift <= requiredWidth; xShift += gridStepX) {
g.drawLine(xShift, 0, xShift, (int) height);
if (xShift > 0) {
String xLabels = timeAxes > 0 ? "-" + timeAxes : "" + timeAxes;
g.drawString(xLabels, xShift - (fm.stringWidth(xLabels) / 2), (int) height + 2 * 100);
}
timeAxes -= 500;
}
// Paint horizontal lines.
double i = 0;
for (int yShift = requiredHeight; yShift >= 0; yShift -= gridStepY) {
String yLabels = DECIMAL_FORMAT.format(i);
g.drawLine(0, yShift, requiredWidth, yShift);
g.drawString(yLabels, - fm.stringWidth(yLabels) - 1 * 100, yShift);
i += 0.1;
}
}
/**
* @param g
* @param dataPosition2D
* @param height
*/
private void plot(PlotContext plotContext, DataDigital dataDigital, int height, double xPosition) {
Graphics2D g = plotContext.getGraphics();
g.setStroke(new BasicStroke(30f));
g.setFont(g.getFont().deriveFont(1.8f * 100));
g.setColor(Color.YELLOW);
g.drawLine((int) xPosition, (int) height, (int) xPosition, 0);
String label = "" + dataDigital.getFlag();
if (dataDigital instanceof DataButton) {
label += ", buttonType=" + ((DataButton) dataDigital).getButtonType();
}
double oppositeLeg = Math.abs(Math.sin(ROTATION_RADIANS) * xPosition);
double adjustment = Math.cos(ROTATION_RADIANS) * xPosition - xPosition;
// Push rotation transform.
plotContext.pushTransform(ROTATION_TRANSFORM);
// Push backwards translation transform.
PAffineTransform translateBackwards = new PAffineTransform();
translateBackwards.setToTranslation(adjustment + 0.5, oppositeLeg - 0.5);
plotContext.pushTransform(translateBackwards);
g.setColor(Color.BLACK);
g.drawString(label, (int) xPosition, 0);
// Pop backwards translation transform.
plotContext.popTransform(translateBackwards);
// Pop rotation transform.
plotContext.popTransform(ROTATION_TRANSFORM);
}
/**
* @param g
* @param dataPosition2D
* @param height
*/
private void plot(PlotContext plotContext, DataString dataString, int height, double xPosition) {
Graphics2D g = plotContext.getGraphics();
g.setStroke(new BasicStroke(30f));
g.setFont(g.getFont().deriveFont(1.8f * 100));
g.setColor(Color.GREEN);
g.drawLine((int) xPosition, (int) height, (int) xPosition, 0);
// String label = "" + dataDigital.getFlag();
// if (dataDigital instanceof DataButton) {
// label += ", buttonType=" + ((DataButton) dataDigital).getButtonType();
// }
double oppositeLeg = Math.abs(Math.sin(ROTATION_RADIANS) * xPosition);
double adjustment = Math.cos(ROTATION_RADIANS) * xPosition - xPosition;
// Push rotation transform.
plotContext.pushTransform(ROTATION_TRANSFORM);
// Push backwards translation transform.
PAffineTransform translateBackwards = new PAffineTransform();
translateBackwards.setToTranslation(adjustment + 0.5, oppositeLeg - 0.5);
plotContext.pushTransform(translateBackwards);
g.setColor(Color.BLACK);
g.drawString(dataString.getData(), (int) xPosition, 0);
// Pop backwards translation transform.
plotContext.popTransform(translateBackwards);
// Pop rotation transform.
plotContext.popTransform(ROTATION_TRANSFORM);
}
}