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    }