package org.newdawn.slick.tools.peditor;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.particles.ConfigurableEmitter;
import org.newdawn.slick.particles.ConfigurableEmitter.LinearInterpolator;
/**
* A visual control to allow the editing of linear interpolated values effecting
* the generated particles.
*
* @author void
*/
public class GraphEditorWindow extends JPanel {
/** The list of values placed in the graph */
private Hashtable values;
/** The properties listed in the top left */
private JComboBox properties;
/** The panel with the main controls on */
private DefaultPanel top;
/** The panel displaying the current graph */
private GraphPanel panel;
/** The x axis label */
private JLabel valueXLabel;
/** The y axis label */
private JLabel valueYLabel;
/** The minimum value for the graph control */
private JSpinner minSpinner;
/** The maxium value for the graph control */
private JSpinner maxSpinner;
/** The color for lines on the graph */
private static Color COLOR_LINE = new Color(0xEEEEEE);
/** The color for points on the graph */
private static Color COLOR_POINT = new Color(0xFFCC66);
/** The color for point outlines on the graph */
private static Color COLOR_POINT_OUTLINE = new Color(0x444444);
/** The color for selectd points on the graph */
private static Color COLOR_SELECTED_POINT = new Color(0xFFFFFF);
/** The color for selectd point outlines on the graph */
private static Color COLOR_SELECTED_POINT_OUTLINE = new Color(0x000000);
/** The color for the legend text on the graph */
private static Color COLOR_LEGEND = new Color(0x000000);
/** The color for the legend background on the graph */
private static Color COLOR_LEGEND_BACKGROUND = new Color(0x555566);
/** The color for the legend grid on the graph */
private static Color COLOR_LEGEND_GRID = new Color(0x333344);
/** The color for the legend grid crosses on the graph */
private static Color COLOR_LEGEND_GRID_CROSS = new Color(0x222233);
/** The color for the background on the graph */
private static Color COLOR_BACKGROUND = new Color(0x444455);
/** The color for the labels on the graph */
private static Color COLOR_LABEL = new Color(0x000000);
/** The x axis label */
private static final String TEXT_CURRENT_X = "Time: ";
/** The y axis label */
private static final String TEXT_CURRENT_Y = "Value: ";
/**
* Create a new graph editor window
*/
public GraphEditorWindow() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBackground(COLOR_BACKGROUND);
// window is divided into the top panel and the graph panel (see below)
panel = new GraphPanel();
top = createTopPanel();
top.setEnabled(false);
add(top);
add(panel);
// init the values
values = new Hashtable();
}
/**
* Set the emitter that is being controlled
*
* @param emitter The emitter that is configured by this panel
*/
public void setLinkedEmitter(ConfigurableEmitter emitter) {
// set the title
Window w = SwingUtilities.windowForComponent(this);
if (w instanceof Frame)
((Frame) w).setTitle("Whiskas Gradient Editor (" + emitter.name
+ ")");
// clear all values
properties.removeAllItems();
values.clear();
panel.setInterpolator(null);
enableControls();
}
/**
* Create the top panel
*
* @return A fully configured component
*/
private DefaultPanel createTopPanel() {
DefaultPanel top = new DefaultPanel();
top.setLayout(new BoxLayout(top, BoxLayout.X_AXIS));
top.setAlignmentX(Component.LEFT_ALIGNMENT);
top.setPreferredSize(new Dimension(Short.MAX_VALUE, 40));
top.add(Box.createRigidArea(new Dimension(5, 0)));
// property label
JLabel propLabel = new JLabel("Property");
propLabel.setBounds(10, 20, 40, 20);
propLabel.setForeground(COLOR_LABEL);
top.add(propLabel);
top.add(Box.createRigidArea(new Dimension(5, 0)));
// add properties combobox
properties = new JComboBox();
properties.setMaximumSize(new Dimension(100, 20));
properties.setMinimumSize(new Dimension(100, 20));
properties.setPreferredSize(new Dimension(100, 20));
properties.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) {
String item = (String) event.getItem();
LinearInterpolator currentValue = (LinearInterpolator) values
.get(item);
panel.setInterpolator(currentValue);
}
});
top.add(properties);
top.add(Box.createRigidArea(new Dimension(10, 0)));
// min label
JLabel minLabel = new JLabel("Min");
minLabel.setMaximumSize(new Dimension(100, 20));
minLabel.setForeground(COLOR_LABEL);
top.add(minLabel);
top.add(Box.createRigidArea(new Dimension(5, 0)));
// min spinner
minSpinner = new JSpinner(new SpinnerNumberModel(0, -1000, 1000, 1));
minSpinner.setMaximumSize(new Dimension(100, 20));
minSpinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
fireUpdated(e.getSource());
}
});
top.add(minSpinner);
top.add(Box.createRigidArea(new Dimension(5, 0)));
// max label
JLabel maxLabel = new JLabel("Max");
maxLabel.setMaximumSize(new Dimension(100, 20));
maxLabel.setForeground(COLOR_LABEL);
top.add(maxLabel);
top.add(Box.createRigidArea(new Dimension(5, 0)));
// max spinner
maxSpinner = new JSpinner(new SpinnerNumberModel(255, -1000, 1000, 1));
maxSpinner.setMaximumSize(new Dimension(100, 20));
maxSpinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
fireUpdated(e.getSource());
}
});
top.add(maxSpinner);
top.add(Box.createRigidArea(new Dimension(5, 0)));
// spacer
top.add(Box.createHorizontalGlue());
// value x label
valueXLabel = new JLabel(TEXT_CURRENT_X);
valueXLabel.setMaximumSize(new Dimension(100, 20));
valueXLabel.setForeground(COLOR_LABEL);
top.add(valueXLabel);
top.add(Box.createRigidArea(new Dimension(10, 0)));
// value y label
valueYLabel = new JLabel(TEXT_CURRENT_Y);
valueYLabel.setMaximumSize(new Dimension(100, 20));
valueYLabel.setForeground(COLOR_LABEL);
top.add(valueYLabel);
top.add(Box.createRigidArea(new Dimension(10, 0)));
return top;
}
/**
* Fire a notification that the panel has been changed
*
* @param control The source of the update
*/
private void fireUpdated(Object control) {
if (control.equals(minSpinner)) {
int minY = ((Integer) minSpinner.getValue()).intValue();
if (minY < panel.getWorldMaxY()) {
panel.setWorldMinY(minY);
panel.makeSureCurveFits();
panel.repaint();
} else {
minSpinner.setValue(new Integer((int) panel.getWorldMinY()));
}
} else if (control.equals(maxSpinner)) {
int maxY = ((Integer) maxSpinner.getValue()).intValue();
if (maxY > panel.getWorldMinY()) {
panel.setWorldMaxY(maxY);
panel.makeSureCurveFits();
panel.repaint();
} else {
maxSpinner.setValue(new Integer((int) panel.getWorldMaxY()));
}
}
}
/**
* Register a configurable value with the graph panel
*
* @param value The value to be registered
* @param name The name to display for this value
*/
public void registerValue(LinearInterpolator value, String name) {
// add to properties combobox
properties.addItem(name);
// add to value map
values.put(name, value);
// set as current interpolator
panel.setInterpolator(value);
// enable all input fields
enableControls();
}
/**
* Enable the controls for the graph
*/
private void enableControls() {
if (properties.getItemCount() > 0)
top.setEnabled(true);
else
top.setEnabled(false);
}
/**
* Remove a configurable value from the graph
*
* @param name The name of the value to be removed
*/
public void removeValue(String name) {
properties.removeItem(name);
values.remove(name);
if (properties.getItemCount() >= 1) {
properties.setSelectedIndex(0);
} else {
panel.setInterpolator(null);
}
enableControls();
}
/**
* Indicate that the first property should be displayed
*/
public void setFirstProperty() {
if (properties.getItemCount() > 0) {
properties.setSelectedIndex(0);
LinearInterpolator currentValue = (LinearInterpolator) values
.get(properties.getSelectedItem());
panel.setInterpolator(currentValue);
}
}
/**
* The actual panel the graph is drawn on
*
* @author void
*/
public class GraphPanel extends JPanel {
/** The list of points */
private ArrayList curve;
/** The value being configured */
private LinearInterpolator value;
/** The graph viewport minimum x value */
private float viewportMinX;
/** The graph viewport maximum x value */
private float viewportMaxX;
/** The graph viewport minimum y value */
private float viewportMinY;
/** The graph viewport maximum y value */
private float viewportMaxY;
/** The graph world minimum x value */
private float worldMinX = 0.0f;
/** The graph world maximum x value */
private float worldMaxX = 1.0f;
/** The graph world minimum y value */
private float worldMinY = 0.0f;
/** The graph world maximum y value */
private float worldMaxY = 255.0f;
/** The border size on the x axis */
private float viewBorderX = 50;
/** The border size of the y axis */
private float viewBorderY = 25;
/** The selected point for colour */
private int colorSelectedPoint;
/** The current point for colour */
private int colorPoint;
/** The mouse X position */
private float mouseX;
/** The mouse Y position */
private float mouseY;
/** The image displayed int he background of the panel */
private BufferedImage backgroundImage;
/**
* Create a new graph panel
*/
public GraphPanel() {
setLayout(null);
// load image
backgroundImage = loadBackgroundImage();
// mouse motion listener to forward events to the gradient
this.addMouseMotionListener(new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
mouseDraggedEvent(e.getX(), e.getY(), e.isShiftDown());
}
public void mouseMoved(MouseEvent e) {
mouseMovedEvent(e.getX(), e.getY());
}
});
// mouse
this.addMouseListener(new MouseListener() {
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
public void mouseClicked(MouseEvent e) {
mousePressedEvent(e.getX(), e.getY(),
e.getButton() == MouseEvent.BUTTON1,
e.getButton() == MouseEvent.BUTTON3,
e.getButton() == MouseEvent.BUTTON2);
}
});
}
/**
* Load a background image and apply it (add some style to whiskas =))
*
* @return The image to display in the background
*/
private BufferedImage loadBackgroundImage() {
InputStream in = ParticleEditor.class.getClassLoader()
.getResourceAsStream(
"org/newdawn/slick/tools/peditor/data/charlie.png");
BufferedImage backgroundImage = null;
try {
backgroundImage = ImageIO.read(in);
} catch (IOException e) {
e.printStackTrace();
}
return backgroundImage;
}
/**
* Set the interpolator being configured
*
* @param value The value to be configured
*/
public void setInterpolator(LinearInterpolator value) {
if (value == null) {
this.value = null;
this.curve = null;
} else {
this.value = value;
curve = convertToCurvePointCurve(value.getCurve());
minSpinner.setValue(new Integer(value.getMin()));
maxSpinner.setValue(new Integer(value.getMax()));
worldMinY = value.getMin();
worldMaxY = value.getMax();
repaint();
}
}
/**
* Change the current curve and make sure that the whole curve is in the
* worldMinY and worldMaxY interval
*/
private void makeSureCurveFits() {
for (int i = 0; i < curve.size(); i++) {
CurvePoint point = ((CurvePoint) curve.get(i));
if (point.y > worldMaxY)
point.y = worldMaxY;
if (point.y < worldMinY)
point.y = worldMinY;
}
}
/**
* Find the first selected point in the given curve
*
* @param curve The list of points to search
* @return The index of the selected point
*/
private int findSelectedPoint(ArrayList curve) {
for (int i = 0; i < curve.size(); i++) {
if (((CurvePoint) curve.get(i)).isSelected()) {
return i;
}
}
return -1;
}
/**
* Return the index of the previous point in the given curve
*
* @param curve The list of points to search
* @param idx The index of the current point
* @return The index of the point that is previous to the point with the
* given index
*/
private int getPrev(ArrayList curve, int idx) {
if (idx == -1)
return -1;
if (idx <= 0 || idx >= curve.size() - 1)
return -1;
return idx - 1;
}
/**
* Return the index of the next point in the given curve
*
* @param curve The list of points to search
* @param idx The index of the current point
* @return The index of the point that follows the point with the given
* index
*/
private int getNext(ArrayList curve, int idx) {
if (idx == -1)
return -1;
if (idx >= curve.size() - 1)
return -1;
return idx + 1;
}
/**
* Drag a curve point based on the reception of an event
*
* @param x The new x position
* @param y The new y position
* @param shiftDown True if shift is pressed
*/
public void mouseDraggedEvent(int x, int y, boolean shiftDown) {
if (!isActive())
return;
int idx = findSelectedPoint(curve);
if (idx != -1) {
CurvePoint selected = (CurvePoint) curve.get(idx);
CurvePoint world = viewToWorld(new CurvePoint(x, y));
// let the first and last points only change in y
if (selected == curve.get(0)
|| selected == curve.get(curve.size() - 1)) {
selected.y = world.y;
} else {
selected.x = world.x;
selected.y = world.y;
}
if (selected.x < worldMinX)
selected.x = worldMinX;
if (selected.x > worldMaxX)
selected.x = worldMaxX;
if (selected.y < worldMinY)
selected.y = worldMinY;
if (selected.y > worldMaxY)
selected.y = worldMaxY;
if (!shiftDown) {
int prev = getPrev(curve, idx);
int next = getNext(curve, idx);
if (prev != -1) {
if (selected.x < ((CurvePoint) curve.get(prev)).x)
selected.x = ((CurvePoint) curve.get(prev)).x;
}
if (next != -1) {
if (selected.x > ((CurvePoint) curve.get(next)).x)
selected.x = ((CurvePoint) curve.get(next)).x;
}
}
mouseX = x;
mouseY = y;
sortPoints();
repaint();
}
}
/**
* Notified that the mouse pointer has moved
*
* @param x The x position of the new mouse position
* @param y The y position of the new mouse position
*/
public void mouseMovedEvent(int x, int y) {
if (!isActive())
return;
CurvePoint posView = new CurvePoint(x, y);
for (int i = 0; i < curve.size(); i++) {
CurvePoint pWorld = (CurvePoint) curve.get(i);
CurvePoint pView = worldToView((CurvePoint) curve.get(i));
if ((posView.x > (pView.x - 12 / 2))
&& (posView.x < (pView.x + 12 / 2))
&& (posView.y > (pView.y - 12 / 2))
&& (posView.y < (pView.y + 12 / 2))) {
((CurvePoint) curve.get(i)).selected = true;
} else {
((CurvePoint) curve.get(i)).selected = false;
}
}
mouseX = x;
mouseY = y;
repaint();
}
/**
* Notification that the mouse was pressed on the panel
*
* @param leftButton True if the left button was pressed
* @param rightButton True if the right button was pressed
* @param middleButton True if the middle button was pressed
* @param x The x position of the mouse press
* @param y The y position of the mouse press
*/
public void mousePressedEvent(int x, int y, boolean leftButton,
boolean rightButton, boolean middleButton) {
if (!isActive())
return;
// right button -> remove selected point
if (rightButton) {
int idx = findSelectedPoint(curve);
if (idx != -1) {
// can't remove first and last points
if (idx != 0 && idx != curve.size() - 1) {
curve.remove(idx);
repaint();
}
}
} else if (leftButton) {
int idx = findSelectedPoint(curve);
if (idx == -1) {
// only add points when nothing is selected
if (x >= viewportMinX && x <= viewportMaxX
&& y >= viewportMaxY && y <= viewportMinY) {
CurvePoint p = viewToWorld(new CurvePoint(x, y));
curve.add(1, p);
sortPoints();
repaint();
}
}
}
}
/**
* Convert given control point from world to viewport coordinates
*
* @param in The point to convert
* @return A new control point representing the point in view space
*/
private CurvePoint worldToView(CurvePoint in) {
float sx = (viewportMaxX - viewportMinX) / (worldMaxX - worldMinX);
float sy = (viewportMaxY - viewportMinY) / (worldMaxY - worldMinY);
float cx = -sx * worldMinX + viewportMinX;
float cy = -sy * worldMinY + viewportMinY;
return new CurvePoint(sx * in.x + cx, sy * in.y + cy);
}
/**
* Convert the given point in view space into world space
*
* @param view The point to convert
* @return A new control point that represent the point in world space
*/
public CurvePoint viewToWorld(CurvePoint view) {
float sx = (viewportMaxX - viewportMinX) / (worldMaxX - worldMinX);
float sy = (viewportMaxY - viewportMinY) / (worldMaxY - worldMinY);
float cx = -sx * worldMinX + viewportMinX;
float cy = -sy * worldMinY + viewportMinY;
return new CurvePoint((view.x - cx) / sx, (view.y - cy) / sy);
}
/**
* Draw the legen of the graph
*
* @param g The graphics context on which to draw the legend
* @param vx0 The view top-left x coordinate
* @param vy0 The view top-left y coordinate
* @param vx1 The view bottom-right x coordinate
* @param vy1 The view bottom-right y coordinate
*/
private void drawLegend(Graphics2D g, float vx0, float vy0, float vx1,
float vy1) {
g.setColor(COLOR_BACKGROUND);
g.fillRect((int) vx0, (int) (vy1 - viewBorderY),
(int) (vx1 - vx0 + viewBorderX),
(int) (vy0 - vy1 + viewBorderY * 2));
int legendLineMain = (int) viewBorderY;
int legendLineMin = 8;
g.setColor(COLOR_LEGEND_BACKGROUND);
g.fillRect((int) vx0, (int) vy0, (int) (vx1 - vx0 + viewBorderX),
(int) (vy0));
// x line bottom (min y)
g.setColor(COLOR_LEGEND);
g.drawLine((int) vx0, (int) vy0, (int) (vx1 + viewBorderX),
(int) vy0);
g.drawLine((int) vx0, (int) vy0 + 1, (int) (vx1 + viewBorderX),
(int) vy0 + 1);
// let's first calculate how much space is available
float space = vx1 - vx0;
// now estimate that we need at least pixel per interval
int intervalCountX = ((int) space / 50);
if ((intervalCountX & 1) == 1)
intervalCountX--;
// x
for (int x = 0; x < intervalCountX + 1; x++) {
float xi0 = (worldMaxX - worldMinX) / intervalCountX
* x;
CurvePoint c = worldToView(new CurvePoint(xi0, 0));
c.y = vy0;
g.setColor(COLOR_LEGEND);
g.drawLine((int) c.x, (int) c.y, (int) c.x, (int) c.y
+ legendLineMain);
g.setColor(COLOR_LEGEND_GRID);
g.drawLine((int) c.x, (int) (viewportMaxY - viewBorderY),
(int) (c.x), (int) (viewportMinY));
String text = convertFloat(xi0 * 1.0f);
g.setColor(COLOR_LEGEND);
g
.drawString(text, (int) c.x + 2, (int) (c.y
+ viewBorderY - 2));
if (x <= (intervalCountX - 1)) {
for (int xsub = 0; xsub < 2; xsub++) {
float xi1 = (worldMaxX - worldMinX)
/ intervalCountX * (x + 1);
CurvePoint ci = worldToView(new CurvePoint((xi0 + xi1)
/ 2.0f * xsub, 0));
ci.y = vy0;
g.setColor(COLOR_LEGEND);
g.drawLine((int) ci.x, (int) ci.y, (int) ci.x,
(int) ci.y + legendLineMin);
}
}
}
// y
g.setColor(COLOR_LEGEND_BACKGROUND);
g.fillRect(0, (int) (vy1 - viewBorderY), (int) vx0,
(int) (vy0 + viewBorderY));
// let's first calculate how much space is available
space = vy0 - vy1;
// now estimate that we need at least pixel per interval
int intervalCountY = ((int) space / 30);
if ((intervalCountY & 1) == 1)
intervalCountY--;
// y
int legendLineMainY = 50;
for (int y = 0; y < intervalCountY + 1; y++) {
float yi0 = (worldMaxY - worldMinY) / intervalCountY
* y + worldMinY;
CurvePoint c = worldToView(new CurvePoint(0, yi0));
// legend line
g.setColor(COLOR_LEGEND);
g.drawLine((int) c.x - legendLineMainY, (int) c.y, (int) c.x,
(int) c.y);
// grid line
g.setColor(COLOR_LEGEND_GRID);
g.drawLine((int) c.x, (int) (c.y), (int) (c.x + viewportMaxX),
(int) (c.y));
// legend text
String text = convertFloat(yi0);
g.setColor(COLOR_LEGEND);
g.drawString(text, (int) vx0
- g.getFontMetrics().stringWidth(text) - 2,
(int) (c.y - 2));
}
// x line top (max y)
g.setColor(COLOR_LEGEND);
g.drawLine((int) vx0, (int) (vy1), (int) (vx1 + viewBorderX),
(int) (vy1));
// left
g.drawLine((int) vx0, (int) vy0, (int) vx0,
(int) (vy1 - viewBorderY));
// right
g.drawLine((int) vx1, (int) vy0, (int) vx1,
(int) (vy1 - viewBorderY));
}
/**
* Convert the list of the given CurvePoint
*
* @param curve The list of points to convert
* @return The new list of points in Point2D format
*/
private ArrayList convertToPoint2DCurve(ArrayList curve) {
ArrayList bla = new ArrayList();
for (int j = 0; j < curve.size(); j++) {
CurvePoint c = (CurvePoint) curve.get(j);
bla.add(new Vector2f(c.getX(), c.getY()));
}
return bla;
}
/**
* Convert a point 2d curve into a curve point curve
*
* @param point2DCurve The list of points to convert
* @return The list of points in CurvePoint format
*/
private ArrayList convertToCurvePointCurve(ArrayList point2DCurve) {
ArrayList curvePointCurve = new ArrayList();
for (int j = 0; j < point2DCurve.size(); j++) {
Vector2f c = (Vector2f) point2DCurve.get(j);
curvePointCurve.add(new CurvePoint(c.getX(), c.getY()));
}
return curvePointCurve;
}
/**
* Paint the display
*
* @param g The graphics context to draw onto
* @param vx0 The top-left x coordinate
* @param vy0 The top-left y coordinate
* @param vx1 The bottom-right x coordinate
* @param vy1 The bottom-right y coordinate
*/
public void paint(Graphics2D g, float vx0, float vy0, float vx1, float vy1) {
if (!isActive()) {
g.drawImage(backgroundImage, 0, (int) vy1
- backgroundImage.getHeight(), null);
return;
}
viewportMinX = vx0 + viewBorderX;
viewportMaxX = vx1 - viewBorderX;
viewportMinY = vy1 - viewBorderY;
viewportMaxY = vy0 + viewBorderY;
drawLegend(g, viewportMinX, viewportMinY, viewportMaxX,
viewportMaxY);
g.setColor(COLOR_LEGEND_GRID);
g.drawLine((int) vx0, (int) vy0, (int) (vx1), (int) vy0);
int[] xPoints = new int[curve.size()];
int[] yPoints = new int[curve.size()];
for (int i = 0; i < curve.size(); i++) {
CurvePoint p = worldToView((CurvePoint) curve.get(i));
xPoints[i] = (int) (p.x);
yPoints[i] = (int) (p.y);
}
g.setColor(COLOR_LINE);
g.drawPolyline(xPoints, yPoints, curve.size());
int width = 6;
int height = 6;
for (int i = 0; i < curve.size(); i++) {
Color color;
Color colorOutline;
int off;
if (((CurvePoint) curve.get(i)).selected) {
color = COLOR_SELECTED_POINT;
colorOutline = COLOR_SELECTED_POINT_OUTLINE;
off = 1;
} else {
color = COLOR_POINT;
colorOutline = COLOR_POINT_OUTLINE;
off = 0;
}
g.setColor(colorOutline);
g.fillRect(xPoints[i] - width / 2 - off, yPoints[i] - height
/ 2 - off, width + off * 2, height + off * 2);
g.setColor(color);
g.fillRect(xPoints[i] - width / 2 - off + 1, yPoints[i]
- height / 2 - off + 1, width + off * 2 - 2, height
+ off * 2 - 2);
if (value != null && mouseX >= viewportMinX
&& mouseX <= viewportMaxX && mouseY >= vy0
&& mouseY <= vy1) {
value.setCurve(convertToPoint2DCurve(curve));
value.setMin((int) worldMinY);
value.setMax((int) worldMaxY);
// draw little cross where we are
CurvePoint a = viewToWorld(new CurvePoint(mouseX, 0));
float b = value.getValue(a.getX());
CurvePoint crossPoint = new CurvePoint(a.getX(), b);
CurvePoint p = worldToView(crossPoint);
// draw cross-line where we are
g.setColor(COLOR_LEGEND_GRID_CROSS);
g.drawLine((int) p.getX(), (int) vy0, (int) p.getX(),
(int) vy1);
// update mouse position labels
valueXLabel.setText(TEXT_CURRENT_X
+ convertFloat(crossPoint.getX()));
valueYLabel.setText(TEXT_CURRENT_Y
+ convertFloat(crossPoint.getY()));
}
}
}
/**
* Sort the control points based on their position
*/
private void sortPoints() {
final CurvePoint firstPt = (CurvePoint) curve.get(0);
final CurvePoint lastPt = (CurvePoint) curve.get(curve.size() - 1);
Comparator compare = new Comparator() {
public int compare(Object firstO, Object secondO) {
CurvePoint first = (CurvePoint) firstO;
CurvePoint second = (CurvePoint) secondO;
if (first == firstPt) {
return -1;
}
if (second == lastPt) {
return -1;
}
if (first.x < second.x)
return -1;
else if (first.x > second.x)
return 1;
else
return 0;
}
};
Collections.sort(curve, compare);
}
/**
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
public void paintComponent(Graphics g1d) {
super.paintComponent(g1d);
Graphics2D g = (Graphics2D) g1d;
g.setColor(new Color(0x303040));
g.fillRect(0, 0, this.getWidth(), this.getHeight());
paint(g, 0, 0, this.getWidth(), this.getHeight());
}
/**
* Check if this graph panel is currently being used
*
* @return True if the graph panel is current being used
*/
private boolean isActive() {
return (curve != null);
}
/**
* Convert a float to a string format
*
* @param f The float to convert
* @return The string formatted from the float
*/
private String convertFloat(float f) {
NumberFormat format = NumberFormat.getInstance();
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
format.setMinimumIntegerDigits(1);
format.setMaximumIntegerDigits(5);
return format.format(f);
}
/**
* A point on the curve
*
* @author void
*/
private class CurvePoint {
/** The x coordinate of the point */
public float x;
/** The y coordinate of the point */
public float y;
/** True if this point is selected */
public boolean selected;
/**
* Create a new curve point
*
* @param x The x coordinate of the curve point
* @param y The y coordinate of the curve point
*/
public CurvePoint(float x, float y) {
this.x = x;
this.y = y;
selected = false;
}
/**
* Check if this point is selected
*
* @return True if the point is selected
*/
public boolean isSelected() {
return selected;
}
/**
* Get the x coordinate of this point
*
* @return The x coordinate of this point
*/
public float getX() {
return x;
}
/**
* Get the y coordinate of this point
*
* @return The y coordinate of this point
*/
public float getY() {
return y;
}
}
/**
* Get the world minimum y value
*
* @return The world minimum y value
*/
public float getWorldMinY() {
return worldMinY;
}
/**
* Set the world minimum y value
*
* @param worldMinY The world minimum y value
*/
public void setWorldMinY(float worldMinY) {
this.worldMinY = worldMinY;
}
/**
* Get the world maximum y value
*
* @return The world maximum y value
*/
public float getWorldMaxY() {
return worldMaxY;
}
/**
* Set the world maximum y value
*
* @param worldMaxY The world maximum y value
*/
public void setWorldMaxY(float worldMaxY) {
this.worldMaxY = worldMaxY;
}
}
/**
* Simple test case for the gradient painter
*
* @param argv
* The arguments supplied at the command line
*/
public static void main(String[] argv) {
JFrame frame = new JFrame("Whiskas Gradient Editor");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel main = new JPanel();
main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS));
main.setBackground(Color.CYAN);
frame.getContentPane().add(main);
//
GraphEditorWindow bottom = new GraphEditorWindow();
ArrayList curve = new ArrayList();
curve.add(new Vector2f(0.0f, 0.0f));
curve.add(new Vector2f(1.0f, 255.0f));
LinearInterpolator test = new ConfigurableEmitter("bla").new LinearInterpolator(
curve, 0, 255);
bottom.registerValue(test, "Test");
curve = new ArrayList();
curve.add(new Vector2f(0.0f, 255.0f));
curve.add(new Vector2f(1.0f, 0.0f));
test = new ConfigurableEmitter("bla").new LinearInterpolator(curve, 0,
255);
bottom.registerValue(test, "Test 2");
main.add(bottom);
frame.pack();
frame.setVisible(true);
frame.setSize(600, 300);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
}