// Graph for floats in range [0, 1] // // Author Matthew Caryl // Created 24.3.97 // // Although under copywrite to the author (Matthew Caryl) this code can be copied and modified for non-commercial // purposes as long as any derivatives contain this condition. package Swarm; import java.awt.Panel; import java.awt.Event; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Font; import java.lang.String; import java.lang.Throwable; final class Graph extends Panel { // // Private interface // private static Dimension dimensions = new Dimension(150, 100); private static int TICK = 3; private static int DAT = TICK; private static int DOT = 2 * DAT; private World owner; private Graphics graphics; private Font font; private String name; private int name_x, name_y; private float[] values; private Color lineColor; private Image image; private Graphics imageGraphics; private int dx; private int top, height; private int selected_element = -1; // x position of column private int putX(int element) { return TICK + dx / 2 + dx * element; } // y position of unit private int putY(int element) { return top + (int) ((1f - values[element]) * height); } // unit from y position private float getY(int Y) { return 1f - (float) (Y - top) / (float) height; } // draw value line private void drawLine(Graphics g, int start, int finish) { g.drawLine(putX(start), putY(start), putX(finish), putY(finish)); } // draw value line private void drawDot(Graphics g, int element) { g.drawRect(putX(element) - DAT, putY(element) - DAT, DOT, DOT); } // draw value line private void drawGraph(Graphics g) { g.setColor(lineColor); for (int i = values.length - 1; i > 0; i--) drawLine(g, i, i - 1); for (int i = values.length - 1; i >= 0; i--) drawDot(g, i); } // draw line but not exception private void drawGraph(Graphics g, int exception) { g.setColor(lineColor); for (int i = values.length - 1; i > exception + 1; i--) drawLine(g, i, i - 1); for (int i = exception - 1; i > 0; i--) drawLine(g, i, i - 1); for (int i = values.length - 1; i >= exception + 1; i--) drawDot(g, i); for (int i = exception - 1; i >= 0; i--) drawDot(g, i); } // draw exception private void drawException(Graphics g, int exception) { g.setColor(lineColor); if (exception > 0) drawLine(g, exception - 1, exception); if (exception < values.length - 1) drawLine(g, exception, exception + 1); drawDot(g, exception); } // blank background with graph axis private void paintBackground(Graphics g) { g.setColor(this.getBackground()); g.fillRect(0, 0, size().width, size().height); g.setColor(this.getForeground()); g.setFont(font); g.drawString(name, name_x, name_y); g.drawLine(TICK, top, TICK, top + height); g.drawLine(TICK, top + height, TICK + values.length * dx, top + height); g.drawLine(0, top, TICK, top); g.drawLine(0, top + height, TICK, top + height); for (int i = values.length - 1; i >= 0 ; i--) g.drawLine(TICK + i * dx, top + height, TICK + i * dx, size().height); } // // Package interface // // move values towards those given, return false if no movement public boolean slideTowards(float[] V) { boolean working = false; for (int i = values.length - 1; i >= 0; i--) { if (values[i] == V[i]) continue; float d = V[i] - values[i]; if (d > 0.05f) values[i] += 0.05f; else if (d < -0.05f) values[i] -= 0.05f; else values[i] += d; working = true; } if (working) repaint(); return working; } // // Public interface // public Graph(World W, String N, Color C, float[] V) { owner = W; name = N; values = V; lineColor = C; } // destroy graphics objects public void finalize() throws Throwable { if (graphics != null) graphics.dispose(); if (imageGraphics != null) imageGraphics.dispose(); super.finalize(); } // paint using background buffer public void paint(Graphics g) { graphics.drawImage(image, 0, 0, this); drawGraph(g); } // find if mouse down in dot public boolean mouseDown(Event e, int X, int Y) { if (selected_element == -2) return true; Rectangle r = new Rectangle(); for (int i = values.length - 1; i >= 0; i--) { int x = putX(i); int y = putY(i); r.reshape(x - DAT, y - DAT, DOT, DOT); if (!r.inside(X, Y)) continue; // if so then set up for dragging drawGraph(imageGraphics, i); selected_element = i; owner.stop(); return true; } return true; } // drag dot and change values public boolean mouseDrag(Event e, int X, int Y) { if (selected_element < 0) return true; graphics.drawImage(image, 0, 0, this); values[selected_element] = Math.max(0f, Math.min(0.99f, getY(Y))); drawException(graphics, selected_element); return true; // Originally this class was intended for use with linked graphs, // that is when one was moved the others would respond in the opposite // direction. Here is some of the code used to do this. // // graphics.drawImage(image, 0, 0, this); // float old_value = values[element]; // float new_value = Math.max(0f, Math.min(0.99f, getY(Y))); // for (int i = partners.length - 1; i >= 0; i--) // partners[i].newPercentage(element, old_value, new_value); // adjust others to new percentage // float total = new_value; // for (int i = partners.length - 1; i >= 0; i--) // total += partners[i].value(element); // values[element] = new_value; // for (int i = partners.length - 1; i >= 0; i--) { // adjust for cumulative rounding errors // partners[i].addPercentage(element, (1f - total) / (float) partners.length); // partners[i].dragLine(element); // } // drawException(g, element); } // stop dragging and refresh background buffer public boolean mouseUp(Event e, int X, int Y) { if (selected_element == -2) return true; selected_element = -1; paintBackground(imageGraphics); owner.start(); return true; } // do not respond to mouse events public void disable() { selected_element = -2; } // respond to mouse events public void enable() { selected_element = -1; } // calculate frequently used values, dispose graphics, refresh background buffer public synchronized void reshape(int x, int y, int w, int h) { super.reshape(x, y, w, h); if (graphics != null) graphics.dispose(); graphics = this.getGraphics(); font = graphics.getFont(); top = graphics.getFontMetrics(font).getHeight(); name_x = (w - graphics.getFontMetrics(font).stringWidth(name)) / 2; name_y = top; height = h - top - TICK - 1; dx = (w - TICK) / values.length; if (imageGraphics != null) imageGraphics.dispose(); image = createImage(size().width, size().height); imageGraphics = image.getGraphics(); paintBackground(imageGraphics); } public Dimension preferredSize() { return dimensions; } public Dimension minimumSize() { return dimensions; } }