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 005 package graphlab.graph.graph; 006 007 import graphlab.graph.event.GraphModelListener; 008 import graphlab.library.util.Pair; 009 010 import java.awt.geom.Line2D; 011 import java.awt.geom.Rectangle2D; 012 import java.util.Iterator; 013 014 //todo: it can not handle the cas hat vertex positions are changed. unusable 015 /** 016 * This class puts graph elements in a grid (for example in a 10*10 grid) so that searching for the nearest 017 * element to a point of graph performs faster, larger grid will have faster searches, 018 * it does not guaranteed to have a fast search at every situation, but overally in a grid of size n (a n*n rectangle) the 019 * search for the vertex on the given point will finish in V(G)/(n*n) steps. for an edge it will be some thing near E(G)/(n*n) 020 * (it can be larger up to E(G)/(n) because edges aare lines and can fill more than one cell in the grid.) 021 * In the worst case the search for the vertex will be run in V(G) steps and for the Edge in E(g) steps. 022 * <p/> 023 * Adding and removing element to/from graph has some cost here. but the cost will not appear until a search for an element 024 * in graph is performed. the cost is small (in average case) when the graph bounds doesn't become larger, but when it become larger the cost is 025 * V(G) (or E(G)) 026 * 027 * @author Azin Azadi 028 */ 029 public class GraphControlGrid implements GraphModelListener { 030 /** 031 * a flag to recreate Grids 032 */ 033 private boolean refresh; 034 035 private GraphModel g; 036 037 private VertexModel fakeVertex; 038 private EdgeModel fakeEdge; 039 040 VertexModel[] verticesGrid[][]; 041 042 043 EdgeModel[] edgesGrid[][]; 044 045 int planeDivisions = 10; 046 047 Rectangle2D.Double gbounds; 048 049 050 public GraphControlGrid(GraphModel g) { 051 g.addGraphListener(this); 052 this.g = g; 053 refresh = true; 054 fakeVertex = new VertexModel(); 055 VertexModel _fakeVertex = new VertexModel(); 056 fakeVertex.setLocation(new GraphPoint(Double.MAX_VALUE, Double.MAX_VALUE)); 057 _fakeVertex.setLocation(new GraphPoint(Double.MAX_VALUE, Double.MAX_VALUE)); 058 fakeEdge = new EdgeModel(fakeVertex, _fakeVertex); 059 } 060 061 public Pair mindiste(GraphModel g, GraphPoint p) { 062 if (refresh) { 063 refresh(); 064 refresh = false; 065 } 066 if (p.x < gbounds.x || p.y < gbounds.y || p.x > gbounds.x + gbounds.width || p.y > gbounds.y + gbounds.height) 067 return new Pair<EdgeModel, Double>(null, 100000d); 068 int ix = (int) ((p.x - gbounds.x) / gbounds.width * planeDivisions); 069 int iy = (int) ((p.y - gbounds.y) / gbounds.height * planeDivisions); 070 071 if (p.x < gbounds.x) 072 ix = 0; 073 if (p.y < gbounds.y) 074 iy = 0; 075 if (p.x > gbounds.x + gbounds.width) 076 ix = planeDivisions - 1; 077 if (p.y > gbounds.y + gbounds.height) 078 iy = planeDivisions - 1; 079 080 double min = 100000; 081 EdgeModel mine = null; 082 EdgeModel[] ei = edgesGrid[ix][iy]; 083 for (EdgeModel e : ei) { 084 Line2D.Double l = new Line2D.Double(e.source.getLocation().x, e.source.getLocation().y, e.target.getLocation().x, e.target.getLocation().y); 085 double dist = l.ptLineDistSq(p); 086 if (min > dist) { 087 min = dist; 088 mine = e; 089 } 090 } 091 return new Pair(mine, min); 092 } 093 094 public Pair<VertexModel, Double> mindistv(GraphPoint p) { 095 if (refresh) { 096 refresh(); 097 refresh = false; 098 } 099 100 int ix = (int) ((p.x - gbounds.x) / gbounds.width * planeDivisions); 101 int iy = (int) ((p.y - gbounds.y) / gbounds.height * planeDivisions); 102 103 if (p.x < gbounds.x) 104 ix = 0; 105 if (p.y < gbounds.y) 106 iy = 0; 107 if (p.x > gbounds.x + gbounds.width) 108 ix = planeDivisions - 1; 109 if (p.y > gbounds.y + gbounds.height) 110 iy = planeDivisions - 1; 111 112 double min = 100000; 113 VertexModel minv = null; 114 VertexModel[] vv = verticesGrid[ix][iy]; 115 for (VertexModel v : vv) { 116 double dist = Math.pow(v.getLocation().x - p.x, 2) + Math.pow(v.getLocation().y - p.y, 2); 117 if (min > dist) { 118 min = dist; 119 minv = v; 120 } 121 } 122 if (minv == null) 123 System.out.println("minv: null"); 124 else 125 System.out.println("minv: " + minv.toString()); 126 return new Pair<VertexModel, Double>(minv, min); 127 } 128 129 private void refresh() { 130 verticesGrid = new VertexModel[planeDivisions][planeDivisions][0]; 131 gbounds = g.getZoomedBounds(); 132 if (gbounds.width == 0) 133 gbounds.width = 1; 134 if (gbounds.height == 0) 135 gbounds.height = 1; 136 for (VertexModel v : g) { 137 addVertexToGrid(v); 138 } 139 140 edgesGrid = new EdgeModel[planeDivisions][planeDivisions][0]; 141 Iterator<EdgeModel> ie = g.edgeIterator(); 142 while (ie.hasNext()) { 143 EdgeModel e = ie.next(); 144 addEdgeToGrid(e); 145 } 146 147 } 148 149 150 private void addVertexToGrid(VertexModel v) { 151 GraphPoint loc = v.getLocation(); 152 int ix = (int) ((loc.x - gbounds.x) / gbounds.width * planeDivisions); 153 int iy = (int) ((loc.y - gbounds.y) / gbounds.height * planeDivisions); 154 addVertexToGrid(ix, iy, v); 155 } 156 157 private void removeVertexFromGrid(VertexModel v) { 158 GraphPoint loc = v.getLocation(); 159 int ix = (int) ((loc.x - gbounds.x) / gbounds.width * planeDivisions); 160 int iy = (int) ((loc.y - gbounds.y) / gbounds.height * planeDivisions); 161 VertexModel[] s = verticesGrid[ix][iy]; 162 for (int i = 0; i < s.length; i++) { 163 if (s[i] == v) { 164 s[i] = fakeVertex; 165 return; 166 } 167 } 168 } 169 170 private void addEdgeToGrid(EdgeModel e) { 171 GraphPoint loc1 = e.source.getLocation(); 172 GraphPoint loc2 = e.source.getLocation(); 173 int ix1 = (int) ((loc1.x - gbounds.x) / gbounds.width * planeDivisions); 174 int iy1 = (int) ((loc1.y - gbounds.y) / gbounds.height * planeDivisions); 175 int ix2 = (int) ((loc2.x - gbounds.x) / gbounds.width * planeDivisions); 176 int iy2 = (int) ((loc2.y - gbounds.y) / gbounds.height * planeDivisions); 177 178 int dy = iy2 - iy1; 179 int dx = ix2 - ix1; 180 int maxCells = Math.abs(dx) + Math.abs(dy); 181 addEdgeToGrid(ix1, iy1, e); 182 int prvx = ix1, prvy = iy1; 183 for (int i = 0; i < maxCells; i++) { //go through the line from 1 to 2 and put the edge on the grid 184 double alpha = i / (double) maxCells; 185 int x = (int) (alpha * ix2 + (1 - alpha) * ix1); 186 int y = (int) (alpha * iy2 + (1 - alpha) * iy1); 187 if (x != prvx || y != prvy) { 188 addEdgeToGrid(x, y, e); 189 prvx = x; 190 prvy = y; 191 } 192 } 193 } 194 195 private void addEdgeToGrid(int ix, int iy, EdgeModel e) { 196 EdgeModel[] s = edgesGrid[ix][iy]; 197 if (s == null) 198 s = new EdgeModel[0]; 199 boolean found = false; 200 for (int i = 0; i < s.length; i++) { 201 if (s[i] == fakeEdge) { 202 s[i] = e; 203 found = true; 204 break; 205 } 206 } 207 if (!found) { //there were no fake edges so make the array larger 208 EdgeModel _[] = new EdgeModel[s.length + 2]; 209 System.arraycopy(s, 0, _, 0, s.length); 210 _[s.length] = e; 211 _[s.length + 1] = fakeEdge; //make it a little more larger for better performance, similar to Vectors 212 edgesGrid[ix][iy] = _; 213 } 214 } 215 216 private void addVertexToGrid(int ix, int iy, VertexModel v) { 217 VertexModel[] s = verticesGrid[ix][iy]; 218 if (s == null) 219 s = new VertexModel[0]; 220 boolean found = false; 221 for (int i = 0; i < s.length; i++) { 222 if (s[i] == fakeVertex) { 223 s[i] = v; 224 found = true; 225 break; 226 } 227 } 228 if (!found) { //there were no fake edges so make the array larger 229 VertexModel[] _ = new VertexModel[s.length + 2]; 230 System.arraycopy(s, 0, _, 0, s.length); 231 _[s.length] = v; 232 _[s.length + 1] = fakeVertex; //make it a little more larger for better performance, similar to Vectors 233 verticesGrid[ix][iy] = _; 234 } 235 } 236 237 private void removeEdgeFromGrid(EdgeModel e) { 238 GraphPoint loc1 = e.source.getLocation(); 239 GraphPoint loc2 = e.source.getLocation(); 240 int ix1 = (int) ((loc1.x - gbounds.x) / gbounds.width * planeDivisions); 241 int iy1 = (int) ((loc1.y - gbounds.y) / gbounds.height * planeDivisions); 242 int ix2 = (int) ((loc2.x - gbounds.x) / gbounds.width * planeDivisions); 243 int iy2 = (int) ((loc2.y - gbounds.y) / gbounds.height * planeDivisions); 244 245 int dy = iy2 - iy1; 246 int dx = ix2 - ix1; 247 int maxCells = Math.abs(dx) + Math.abs(dy); 248 removeEdge(edgesGrid[ix1][iy1], e); 249 int prvx = ix1, prvy = iy1; 250 for (int i = 0; i < maxCells; i++) { //go through the line from 1 to 2 and put the edge on the grid 251 double alpha = i / (double) maxCells; 252 int x = (int) (alpha * ix2 + (1 - alpha) * ix1); 253 int y = (int) (alpha * iy2 + (1 - alpha) * iy1); 254 if (x != prvx || y != prvy) { 255 EdgeModel[] s = edgesGrid[x][y]; 256 removeEdge(s, e); 257 prvx = x; 258 prvy = y; 259 } 260 } 261 } 262 263 private void removeEdge(EdgeModel[] s, EdgeModel e) { 264 for (int ii = 0; ii < s.length; ii++) { 265 if (s[ii] == e) { 266 s[ii] = fakeEdge; 267 break; 268 } 269 } 270 } 271 272 273 public void vertexAdded(VertexModel v) { 274 Rectangle2D.Double tbounds = g.getZoomedBounds(); 275 if (tbounds.x < gbounds.x || tbounds.y < gbounds.y || tbounds.width > gbounds.width || tbounds.height > gbounds.height) 276 refresh = true; 277 else { 278 addVertexToGrid(v); 279 } 280 281 } 282 283 public void vertexRemoved(VertexModel v) { 284 removeVertexFromGrid(v); 285 } 286 287 public void edgeAdded(EdgeModel e) { 288 Rectangle2D.Double tbounds = g.getZoomedBounds(); 289 if (tbounds.x < gbounds.x || tbounds.y < gbounds.y || tbounds.width > gbounds.width || tbounds.height > gbounds.height) 290 refresh = true; 291 else { 292 addEdgeToGrid(e); 293 } 294 } 295 296 public void edgeRemoved(EdgeModel e) { 297 removeEdgeFromGrid(e); 298 } 299 300 public void graphCleared() { 301 refresh = true; 302 } 303 304 public void repaintGraph() { 305 refresh = true; 306 } 307 308 }