001 // GraphLab Project: http://graphlab.sharif.edu 002 // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology 003 // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/ 004 package graphlab.graph.graph; 005 006 import graphlab.graph.event.EdgeEvent; 007 import graphlab.graph.event.GraphControlListener; 008 import graphlab.graph.event.GraphEvent; 009 import graphlab.graph.event.VertexEvent; 010 import graphlab.library.util.Pair; 011 import graphlab.platform.core.BlackBoard; 012 013 import javax.swing.*; 014 import java.awt.event.MouseEvent; 015 import java.awt.event.MouseListener; 016 import java.awt.event.MouseMotionListener; 017 import java.awt.event.MouseWheelListener; 018 import java.awt.geom.Line2D; 019 import java.util.Iterator; 020 021 /** 022 * @author Azin Azadi, roozbeh ebrahimi, Ali Ershadi 023 */ 024 public class GraphControl implements MouseListener, MouseWheelListener, MouseMotionListener { 025 private GraphModel g; 026 private JPanel gv; 027 BlackBoard blackboard; 028 private GraphControlListener listener; 029 private VertexModel lastVertexPressed = null; 030 private EdgeModel lastEdgePressed = null; 031 GraphPoint p = new GraphPoint(); 032 // boolean edgesCurved; 033 034 public static final int EDGE_CURVE_CPNTROL_BOX_DIAMETER = 10; 035 /** 036 * just a short cut to EDGE_CURVE_CPNTROL_BOX_DIAMETER 037 */ 038 private static final int cd = EDGE_CURVE_CPNTROL_BOX_DIAMETER; 039 /** 040 * determines the min x and y of graph bounds 041 */ 042 double minx = 0; 043 double miny = 0; 044 045 public void setListener(GraphControlListener l) { 046 this.listener = l; 047 } 048 049 public GraphControl(GraphModel g, JPanel gv, BlackBoard bb) { 050 this.g = g; 051 this.gv = gv; 052 blackboard = bb; 053 gv.addMouseListener(this); 054 gv.addMouseMotionListener(this); 055 gv.addMouseWheelListener(this); 056 } 057 058 //todo: single click != double click 059 060 public void mouseClicked(MouseEvent mouseEvent) { 061 Pair<VertexModel, Double> p = mindistv(g, mousePos(mouseEvent)); 062 VertexModel v = (VertexModel) p.first; 063 if (v != null && isPointOnVertex(g, v, mousePos(mouseEvent))) { 064 if (mouseEvent.getClickCount() > 1) 065 sendEventToBlackBoard(VertexEvent.doubleClicked(v, mousePos(mouseEvent, v), mouseEvent.getButton(), mouseEvent.getModifiersEx())); 066 else 067 sendEventToBlackBoard(VertexEvent.clicked(v, mousePos(mouseEvent, v), mouseEvent.getButton(), mouseEvent.getModifiersEx())); 068 return; 069 } 070 Pair<EdgeModel, Double> pp; 071 pp = mindiste(g, mousePos(mouseEvent)); 072 EdgeModel e = (EdgeModel) pp.first; 073 double dist = (Double) pp.second; 074 if (g.isEdgesCurved()) { 075 if (pp.second <= (EDGE_CURVE_CPNTROL_BOX_DIAMETER)) { 076 sendEventToBlackBoard(EdgeEvent.clicked(e, mousePos(mouseEvent, e), mouseEvent.getButton())); 077 return; 078 } 079 } else if (Math.sqrt(dist) < 4) { 080 // if (doubleClicked) 081 // sendEventToBlackBoard(EdgeEvent.mouseDoubleClicked(e, mousePos(mouseEvent), mouseEvent.getButton())); 082 // else 083 // System.err.println("&^&^&^&^&^&^&"); 084 sendEventToBlackBoard(EdgeEvent.clicked(e, mousePos(mouseEvent, e), mouseEvent.getButton())); 085 return; 086 } 087 088 089 sendEventToBlackBoard(GraphEvent.mouseClicked(g, mousePos(mouseEvent), mouseEvent.getButton(), mouseEvent.getModifiersEx())); 090 } 091 092 093 /** 094 * 0 1 2 3 4 095 * 0 1 2 3 4 096 * 097 * @param mouseEvent 098 * @param e 099 * @return 100 */ 101 private GraphPoint mousePos(MouseEvent mouseEvent, EdgeModel e) { 102 return new GraphPoint(mousePos(mouseEvent).x - e.source.getLocation().x, mousePos(mouseEvent).y - e.source.getLocation().y); 103 } 104 105 private GraphPoint mousePos(MouseEvent mouseEvent, VertexModel v) { 106 return new GraphPoint(mousePos(mouseEvent).x - v.getLocation().x, mousePos(mouseEvent).y - v.getLocation().y); 107 } 108 109 /** 110 * the zoom factor of graph, It will be synced on calling mousePos(mouseEvent) 111 */ 112 double zf; 113 114 private GraphPoint mousePos(MouseEvent mouseEvent) { 115 zf = g.getZoomFactor(); 116 GraphPoint p = new GraphPoint(mouseEvent.getX() / zf, mouseEvent.getY() / zf); 117 //the graphics moves automatically to solve the swing JScrollPane negative positions problem, Im not sure 118 119 if (minx < 0) 120 p.x += minx / zf; 121 if (miny < 0) 122 p.y += miny / zf; 123 return p; 124 } 125 126 public void mouseEntered(java.awt.event.MouseEvent mouseEvent) { 127 sendEventToBlackBoard(GraphEvent.mouseEntered(g, mousePos(mouseEvent), mouseEvent.getButton(), mouseEvent.getModifiersEx())); 128 } 129 130 public void mouseExited(java.awt.event.MouseEvent mouseEvent) { 131 sendEventToBlackBoard(GraphEvent.mouseExited(g, mousePos(mouseEvent), mouseEvent.getButton(), mouseEvent.getModifiersEx())); 132 } 133 134 public void mousePressed(java.awt.event.MouseEvent mouseEvent) { 135 136 lastVertexPressed = null; 137 138 gv.requestFocusInWindow(); 139 GraphPoint mousePos = mousePos(mouseEvent); 140 Pair p = mindistv(g, mousePos); 141 VertexModel v = (VertexModel) p.first; 142 int mbuton = mouseEvent.getModifiersEx(); 143 if (v != null && isPointOnVertex(g, v, mousePos)) { 144 sendEventToBlackBoard(VertexEvent.draggingStarted(v, mousePos(mouseEvent, v), mouseEvent.getButton(), mbuton)); 145 if (lastVertexPressed != null) System.err.println("last = " + lastVertexPressed.getLabel()); 146 lastVertexPressed = v; 147 return; 148 } 149 lastVertexPressed = null; 150 if (g.isEdgesCurved()) { 151 Pair<EdgeModel, Double> pair = mindiste(g, mousePos); 152 if (pair.first != null) { 153 if (pair.second <= EDGE_CURVE_CPNTROL_BOX_DIAMETER) { 154 lastEdgePressed = pair.first; 155 sendEventToBlackBoard(EdgeEvent.draggingStarted(pair.first, mousePos(mouseEvent, lastEdgePressed), mbuton)); 156 return; 157 } 158 } 159 } 160 161 sendEventToBlackBoard(GraphEvent.mouseDraggingStarted(g, mousePos, mouseEvent.getButton(), mbuton)); 162 } 163 164 165 public void mouseReleased(MouseEvent mouseEvent) { 166 Pair p = mindistv(g, mousePos(mouseEvent)); 167 VertexModel v = (VertexModel) p.first; 168 int mouseButton = mouseEvent.getModifiersEx(); 169 if (v != null && isPointOnVertex(g, v, mousePos(mouseEvent)) && lastVertexPressed != null) { 170 sendEventToBlackBoard(VertexEvent.dropped(v, mousePos(mouseEvent, v), mouseEvent.getButton(), mouseButton)); 171 lastVertexPressed = null; 172 } else if (lastVertexPressed != null) { 173 sendEventToBlackBoard(VertexEvent.released(lastVertexPressed, mousePos(mouseEvent, lastVertexPressed), mouseEvent.getButton(), mouseButton)); 174 lastVertexPressed = null; 175 } else if (lastEdgePressed != null) { 176 sendEventToBlackBoard(EdgeEvent.released(lastEdgePressed, mousePos(mouseEvent, lastEdgePressed), mouseButton)); 177 lastEdgePressed = null; 178 } else 179 sendEventToBlackBoard(GraphEvent.mouseDropped(g, mousePos(mouseEvent), mouseEvent.getButton(), mouseButton)); 180 } 181 182 public void mouseWheelMoved(java.awt.event.MouseWheelEvent mouseWheelEvent) { 183 sendEventToBlackBoard(GraphEvent.mouseWheelMoved(g, mousePos(mouseWheelEvent), mouseWheelEvent.getWheelRotation(), mouseWheelEvent.getModifiersEx())); 184 } 185 186 public void mouseDragged(MouseEvent mouseEvent) { 187 int modex = mouseEvent.getModifiersEx(); 188 if (lastVertexPressed != null) { 189 sendEventToBlackBoard(VertexEvent.dragging(lastVertexPressed, mousePos(mouseEvent, lastVertexPressed), mouseEvent.getButton(), modex)); 190 } else if (lastEdgePressed != null) { 191 sendEventToBlackBoard(EdgeEvent.dragging(lastEdgePressed, mousePos(mouseEvent, lastEdgePressed), modex)); 192 } else 193 sendEventToBlackBoard(GraphEvent.dragging(g, mousePos(mouseEvent), mouseEvent.getButton(), modex)); 194 195 } 196 197 public void mouseMoved(MouseEvent e) { 198 sendEventToBlackBoard(GraphEvent.mouseMoved(g, mousePos(e), e.getButton(), e.getModifiersEx())); 199 } 200 201 private void sendEventToBlackBoard(GraphEvent value) { 202 if (listener != null) 203 listener.ActionPerformed(value); 204 } 205 206 private void sendEventToBlackBoard(VertexEvent value) { 207 if (listener != null) 208 listener.ActionPerformed(value); 209 } 210 211 private void sendEventToBlackBoard(EdgeEvent value) { 212 if (listener != null) 213 listener.ActionPerformed(value); 214 } 215 216 /** 217 * @return the minimum distanse edge and its distance to the given GraphPoint, 218 * If edges are curved the distance will be calculated to Curve Control Points 219 */ 220 public static Pair<EdgeModel, Double> mindiste(GraphModel g, GraphPoint p) { 221 double min = 100000; 222 EdgeModel mine = null; 223 Iterator<EdgeModel> ei = g.lightEdgeIterator(); 224 if (g.isEdgesCurved()) { 225 for (; ei.hasNext();) { 226 EdgeModel e = ei.next(); 227 GraphPoint cnp = e.getCurveControlPoint(); 228 GraphPoint s = e.source.getLocation(); 229 GraphPoint t = e.target.getLocation(); 230 GraphPoint cp = new GraphPoint((s.x + t.x) / 2.0 + cnp.x, (s.y + t.y) / 2.0 + cnp.y); 231 double dist = GraphPoint.distance(cp.x, cp.y, p.x, p.y); 232 if (min > dist) { 233 min = dist; 234 mine = e; 235 } 236 } 237 if (min < EDGE_CURVE_CPNTROL_BOX_DIAMETER) { 238 min = 0; 239 } 240 } else { 241 for (; ei.hasNext();) { 242 EdgeModel e = ei.next(); 243 if (!isInBounds(e, p)) 244 continue; 245 GraphPoint sloc = e.source.getLocation(); 246 GraphPoint tloc = e.target.getLocation(); 247 Line2D.Double l = new Line2D.Double(sloc.x, sloc.y, tloc.x, e.target.getLocation().y); 248 double dist = l.ptLineDistSq(p); 249 if (min > dist) { 250 min = dist; 251 mine = e; 252 } 253 } 254 } 255 return new Pair(mine, min); 256 } 257 258 private static boolean isInBounds(EdgeModel e, GraphPoint p) { 259 GraphPoint l1 = e.source.getLocation(); 260 GraphPoint l2 = e.target.getLocation(); 261 return Math.min(l1.x, l2.x) <= p.x + 5 && Math.max(l1.x, l2.x) >= p.x - 5 && Math.min(l1.y, l2.y) <= p.y + 5 && Math.max(l1.y, l2.y) >= p.y - 5; 262 } 263 264 /** 265 * @return the minimum distance vertex to the given location, and its distanse square(^2). 266 */ 267 public static Pair<VertexModel, Double> mindistv(GraphModel g, GraphPoint p) { 268 double min = 100000; 269 VertexModel minv = null; 270 for (VertexModel v : g) { 271 double dist = Math.pow(v.getLocation().x - p.x, 2) + Math.pow(v.getLocation().y - p.y, 2); 272 if (min > dist) { 273 min = dist; 274 minv = v; 275 } 276 } 277 return new Pair(minv, min); 278 } 279 280 /** 281 * @return True if the given point in on the given vertex 282 */ 283 public static boolean isPointOnVertex(GraphModel g, VertexModel v, GraphPoint p) { 284 double zf = g.getZoomFactor(); 285 GraphPoint l = v.getLocation(); 286 GraphPoint s = v.getSize(); 287 double sx = s.x / zf; 288 double sy = s.y / zf; 289 return l.x - sx / 2 <= p.x && l.x + sx / 2 >= p.x && l.y - sy / 2 <= p.y && l.y + sy / 2 >= p.y; 290 } 291 292 }