package pointsTracker; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Random; import javax.imageio.ImageIO; import javax.swing.Icon; import javax.swing.JComponent; /** * @author John B. Matthews; distribution per GPL. * @modified_by Vivaan Bahl, originally taken from https://sites.google.com/site/drjohnbmatthews/graphpanel */ public class GraphPanel extends JComponent { public int WIDE; public int HIGH; private static final int RADIUS = 35; private static final Random rnd = new Random(); private int radius = RADIUS; private Kind kind = Kind.Circular; private List<Node> nodes = new ArrayList<Node>(); private List<Pin> pins = new ArrayList<Pin>(); private List<Node> selected = new ArrayList<Node>(); private List<Edge> edges = new ArrayList<Edge>(); private Point mousePt = new Point(WIDE / 2, HIGH / 2); private Rectangle mouseRect = new Rectangle(); private boolean selecting = false; private Color lineColor = Color.YELLOW; private int pinMode; private static final double LATLNG_DELTA_Y = 0.0000035; //what change in longitude we see from a 1 pixel y delta private static final double LATLNG_DELTA_X = 0.0000238; //what change in latitude we see from a 1 pixel x delta private static final Point SCHENLEY_STOP_SIGN = new Point(679, 239); private static final double SCHENLEY_STOP_SIGN_LATLNG_X = 40.440148; private static final double SCHENLEY_STOP_SIGN_LATLNG_Y = -79.942296; public static final int PIN_MODE_START = 0; public static final int PIN_MODE_MID = 1; public static final int PIN_MODE_END = 2; public static final int PIN_MODE_OOPS = -1; public GraphPanel(int w, int h) { WIDE = w; HIGH = h; this.setOpaque(true); this.addMouseListener(new MouseHandler()); this.addMouseMotionListener(new MouseMotionHandler()); this.setSize(WIDE, HIGH); pinMode = GraphPanel.PIN_MODE_OOPS; } public List<Pin> getPins() { return pins; } @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D)g; BufferedImage img = null; try { img = ImageIO.read(ClassLoader.getSystemResourceAsStream("courseMap.png")); } catch (IOException e1) { // TODO Auto-generated catch block System.out.println("OOPS OOPS OOPS BAD STUFF HAPPENING"); } g.setColor(new Color(0x00f0f0f0)); g.fillRect(0, 0, getWidth(), getHeight()); g2.drawImage(img, null, 0, 0); drawLines(g2); for (Edge e : edges) { e.draw(g); } for (Node n : nodes) { n.draw(g); g.drawString("" + nodes.indexOf(n), n.getLocation().x + 10, n.getLocation().y + 10); } if (selecting) { g.setColor(Color.darkGray); g.drawRect(mouseRect.x, mouseRect.y, mouseRect.width, mouseRect.height); } } public void addPin(Pin p) { int indexToAdd = 0; if (nodes.size() > 0) { indexToAdd = nodes.size() - 1; if (p instanceof StartPin) { indexToAdd = 0; if (pins.get(0) instanceof StartPin) { pins.remove(0); nodes.remove(0); } pins.add(indexToAdd, p); nodes.add(indexToAdd, new Node(new Point(p.getScreenX(), p.getScreenY()), p.getPinWidth(), p.getPinColor(), Kind.Circular)); // this is the only one that doesn't append to the end ((Display)this.getParent()).updatePinList(); return; } else if(p instanceof EndPin) { if(pins.get(indexToAdd) instanceof EndPin) { pins.remove(indexToAdd); nodes.remove(indexToAdd); } } } pins.add(p); nodes.add(new Node(new Point(p.getScreenX(), p.getScreenY()), p.getPinWidth(), p.getPinColor(), Kind.Circular)); for (int i = 0; i < pins.size(); i++) { if (pins.get(i) instanceof EndPin) { pins.add(pins.remove(i)); nodes.add(nodes.remove(i)); break; } } ((Display)this.getParent()).updatePinList(); repaint(); } private void drawLines(Graphics2D g2) { // TODO Auto-generated method stub g2.setColor(lineColor); g2.setStroke(new BasicStroke(3)); for (int i = 1; i < pins.size(); i++) { Node n1 = nodes.get(i); Node n2 = nodes.get(i - 1); g2.drawLine(n1.getLocation().x, n1.getLocation().y, n2.getLocation().x, n2.getLocation().y); } g2.setStroke(new BasicStroke(2)); } public void setPinMode(int setTo) { pinMode = setTo; } public void removeSelected() { selected = Node.getSelected(nodes, new ArrayList<Node>()); for(int i = 0; i < selected.size(); i++) { pins.remove(nodes.indexOf(selected.get(i))); nodes.remove(selected.get(i)); repaint(); } selected = new ArrayList<Node>(); repaint(); } private double[] latLngForPoint(Point toCompare) { int dx = SCHENLEY_STOP_SIGN.x - toCompare.x; int dy = SCHENLEY_STOP_SIGN.y - toCompare.y; double pinLat = SCHENLEY_STOP_SIGN_LATLNG_X - LATLNG_DELTA_X * (dx); double pinLon = SCHENLEY_STOP_SIGN_LATLNG_Y - LATLNG_DELTA_Y * dy; double[] list = new double[2]; list[0] = pinLat; list[1] = pinLon; return list; } //system code below...don't touch this private class MouseHandler extends MouseAdapter { @Override public void mouseReleased(MouseEvent e) { selecting = false; mouseRect.setBounds(0, 0, 0, 0); e.getComponent().repaint(); ((Display)GraphPanel.this.getParent()).updatePinList(); } @Override public void mousePressed(MouseEvent e) { mousePt = e.getPoint(); if (e.isShiftDown()) { Node.selectToggle(nodes, mousePt); } else if (e.isPopupTrigger()) { Node.selectOne(nodes, mousePt); } else if (Node.selectOne(nodes, mousePt)) { selecting = false; } else { Node.selectNone(nodes); selecting = true; double[] list = latLngForPoint(mousePt); double pinLat = list[0]; double pinLon = list[1]; switch (pinMode) { case GraphPanel.PIN_MODE_START: addPin(new StartPin(mousePt.x, mousePt.y, pinLat, pinLon)); break; case GraphPanel.PIN_MODE_MID: addPin(new Pin(mousePt.x, mousePt.y, pinLat, pinLon)); break; case GraphPanel.PIN_MODE_END: addPin(new EndPin(mousePt.x, mousePt.y, pinLat, pinLon)); break; default: System.out.println("OOPS OOPS OOPS"); break; } } e.getComponent().repaint(); } } private class MouseMotionHandler extends MouseMotionAdapter { Point delta = new Point(); @Override public void mouseDragged(MouseEvent e) { if (selecting) { mouseRect.setBounds( Math.min(mousePt.x, e.getX()), Math.min(mousePt.y, e.getY()), Math.abs(mousePt.x - e.getX()), Math.abs(mousePt.y - e.getY())); Node.selectRect(nodes, mouseRect); } else { delta.setLocation( e.getX() - mousePt.x, e.getY() - mousePt.y); Node.updatePosition(nodes, delta); updatePinPositions(); mousePt = e.getPoint(); } e.getComponent().repaint(); } private void updatePinPositions() { // TODO Auto-generated method stub for(Node n : nodes) { Point point = n.getLocation(); int index = nodes.indexOf(n); Pin p = pins.get(index); p.setPinLocation(point); double[] list = latLngForPoint(point); double pinLat = list[0]; double pinLon = list[1]; p.setPinLatLng(pinLat, pinLon); repaint(); } } } /** * The kinds of node in a graph. */ private enum Kind { Circular, Rounded, Square; } /** * An Edge is a pair of Nodes. */ private static class Edge { private Node n1; private Node n2; public Edge(Node n1, Node n2) { this.n1 = n1; this.n2 = n2; } public void draw(Graphics g) { Point p1 = n1.getLocation(); Point p2 = n2.getLocation(); g.setColor(Color.darkGray); g.drawLine(p1.x, p1.y, p2.x, p2.y); } } /** * A Node represents a node in a graph. */ private static class Node { private Point p; private int r; private Color color; private Kind kind; private boolean selected = false; private Rectangle b = new Rectangle(); /** * Construct a new node. */ public Node(Point p, int r, Color color, Kind kind) { this.p = p; this.r = r; this.color = color; this.kind = kind; setBoundary(b); } /** * Calculate this node's rectangular boundary. */ private void setBoundary(Rectangle b) { b.setBounds(p.x - r, p.y - r, 2 * r, 2 * r); } /** * Draw this node. */ public void draw(Graphics g) { g.setColor(this.color); if (this.kind == Kind.Circular) { g.fillOval(b.x, b.y, b.width, b.height); } else if (this.kind == Kind.Rounded) { g.fillRoundRect(b.x, b.y, b.width, b.height, r, r); } else if (this.kind == Kind.Square) { g.fillRect(b.x, b.y, b.width, b.height); } if (selected) { g.setColor(Color.darkGray); g.drawRect(b.x, b.y, b.width, b.height); } } /** * Return this node's location. */ public Point getLocation() { return p; } /** * Return true if this node contains p. */ public boolean contains(Point p) { return b.contains(p); } /** * Return true if this node is selected. */ public boolean isSelected() { return selected; } /** * Mark this node as selected. */ public void setSelected(boolean selected) { this.selected = selected; } /** * Collected all the selected nodes in list. */ public static List<Node> getSelected(List<Node> list, List<Node> selected) { selected.clear(); for (Node n : list) { if (n.isSelected()) { selected.add(n); } } return selected; } /** * Select no nodes. */ public static void selectNone(List<Node> list) { for (Node n : list) { n.setSelected(false); } } /** * Select a single node; return true if not already selected. */ public static boolean selectOne(List<Node> list, Point p) { for (Node n : list) { if (n.contains(p)) { if (!n.isSelected()) { Node.selectNone(list); n.setSelected(true); } return true; } } return false; } /** * Select each node in r. */ public static void selectRect(List<Node> list, Rectangle r) { for (Node n : list) { n.setSelected(r.contains(n.p)); } } /** * Toggle selected state of each node containing p. */ public static void selectToggle(List<Node> list, Point p) { for (Node n : list) { if (n.contains(p)) { n.setSelected(!n.isSelected()); } } } /** * Update each node's position by d (delta). */ public static void updatePosition(List<Node> list, Point d) { for (Node n : list) { if (n.isSelected()) { n.p.x += d.x; n.p.y += d.y; n.setBoundary(n.b); } } } /** * Update each node's radius r. */ public static void updateRadius(List<Node> list, int r) { for (Node n : list) { if (n.isSelected()) { n.r = r; n.setBoundary(n.b); } } } /** * Update each node's color. */ public static void updateColor(List<Node> list, Color color) { for (Node n : list) { if (n.isSelected()) { n.color = color; } } } /** * Update each node's kind. */ public static void updateKind(List<Node> list, Kind kind) { for (Node n : list) { if (n.isSelected()) { n.kind = kind; } } } } private static class ColorIcon implements Icon { private static final int WIDE = 20; private static final int HIGH = 20; private Color color; public ColorIcon(Color color) { this.color = color; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(color); g.fillRect(x, y, WIDE, HIGH); } public int getIconWidth() { return WIDE; } public int getIconHeight() { return HIGH; } } }